10 января 2011 в 20:52

Python-way. Работа над ошибками из песочницы


Всем доброго времени суток! Новогодние праздники позади, все хорошо отдохнули. Тем не мнеее, даже в праздники иногда появляется работа. Мне, к примеру, довелось покопаться в чужом коде на Python. Код хороший, замечательно документирован, но во время чтения не покидало ощущение, что автор читал доки по Python и портировал код с Си-подобного языка. Это меня вдохновило на написание статьи с указанием ошибок, которые неизбежно возникают при переходе на Python с Си-подобных языков.

Статья пригодится тем, кто недавно пишет на этом языке, а также для тех, кто пишет на нем маленькие скрипты, не заморачиваясь на деталях.

Итерация по спискам


for i in range(len(a)):
    print "Под номером %d находится элемент %s" % (i, a[i])

Это работает, но что мы сделали лишнего: посчитали длину списка и создали еще один список, с длиной равной длине списка a. Нас немного спасет xrange, но правильнее от этого не станет. Если вам и правда необходимы индексы элементов, используйте enumerate.

for i, item in enumerate(a):
    print "Под номером %d находится элемент %s" % (i, item)

enumerate(a) не создает лишних списков, он возвращает поочередно элементы списка в виде (<индекс>, <элемент>). Да и выглядит такая конструкция гораздо понятнее.

Если же элементы не нужны, все еще проще. Избегайте вообще проверять без надобности длину списков. Если сама длина вам не нужна, а нужно проверить, не пуст ли спосок, используйте bool(a). Предупреждение: такой прием не подходит для генераторов, bool(a) вернет True, даже если list(a) не содержит элементов.

Обращение к атрибутам


На просторах интернета обнаружил «полезнейший» совет: чтобы обращаться к элементам словаря, как к атрибутам класса, то есть, в стиле JS (myobject.myelement), можно воспользоваться таким приемом:

class mydict(dict):
    def __getattr__(self, key):
        return self[key]
    def __setattr__(self, key, value)
        self[key] = value

Пробуем.

a = mydict(no = "way", bad = "code")
print a.no
# Выведет "way"


Работает! Попробуем добавить еще ключей:

a.update({1:"one", 2:"two"})
a.1
#Ошибка! Атрибут не может начинаться с цифры


Парень, который писал код проекта, хотел, чтобы можно было обращаться как ключам, так и к атрибутам класса, в итоге появился вот такой код:

def __getattr__(self, key):
    try:
        return self.key
    except:
        return self[key]

Как вы можете проверить сами, такой код работать не будет, так как приведет к бесконечной рекурсии: self.key является синтаксическим сахаром для self.__getattr__(key).

Плоское лучше, чем вложенное.


Всегда и безоговорочно.
Посвящается любителям лишних скобок.

def formatName(name):
    if len(name)<40:
        if " " in name:
            if name[0]!="?":
                return name.split(" ")
    return False
	
def formatName(name):
    if len(name)>=40:
        return False
    if " " not in name:
        return False
    if name[0]=="?"
        return False
		
  return name.split(" ")

Второй кусок кода длиннее, но:
— все условия проверки перед работой над объектом перечислены в начале и хорошо просматриваются. Легко можно будет выбросить соответствующее исключение и оно будет рядом с условием, по которому выбросилось.
— основная работа над объектом в конце функции и также хорошо видна. В первом случае она на три уровня глубже.

Удаление элементов из списка


a=range(10);
for item in a:
    if item<5:
        a.remove(item)
print a 
# Вернет [1, 3, 5, 6, 7, 8, 9]


Почему так происходит? Потому что при удалении элемента из списка, индекс не уменьшается. А значит, следующий элемент списка будет пропущен. Отчаявшись, люди идут на такие ухищрения:

i=0
while i<len(a):
    if i<5:
        del a[i]
    else:
        i += 1

Нам на помощь приходит такая замечательная функция как filter(func, a). Она создает новый список из элементов списка, для которым функция func(item) вернет истину.

filter(lambda x:x>=5, a)
# Вернет [6, 7, 8, 9]
[i for i in a if i>=5]
# Также вернет[6, 7, 8, 9], да и выглядит красивее.
print a
# Список a остался неизменным

Пока что на этом все. В cледующий раз речь пойдет о накоротко замкнутых выражениях и lambda-функциях.
Пишите на Python с удовольствием. И помните: особые случаи не настолько особые, чтобы нарушать правила.
@PyVolshebnyi
карма
18,0
рейтинг 0,0
Похожие публикации
Самое читаемое Разработка

Комментарии (191)

  • 0
    Все хорошо, но в __getattr__(self, key) не лучше ли перехватывать исключение KeyError?
    В данном варианте реализации перехватываем любое исключение, насколько я помню так делать не рекомендуется.
    • 0
      Я бы вообще не рекомендовал переобъявлять __getattr__(). Не могу представить, зачем это вообще может понадобиться.
      • 0
        А можно пример от несогласного? =)
        А то так и не узнаю.

        Едиственное, что приходит на ум:

        class mydict(dict):
          self.__getattr__(self, key):
            if key=='smile':
              return "Улыбнитесь, вас снимают! =)"
            else:
              return super(mydict, self).__getattr__(key)
        • +7
          Как-то пример слишком смахивает на «говнокод».

          class mydict(dict):
              @property
              def smile(self):
                  return "Улыбнитесь, вас снимают! =)"
          
      • –1
        Реализация сложной логики на изначально простейших членах объекта (или как они там по-питоньи). Увы и ах, но невозможность ограничить доступ к свойствам (может так?) сподвигает на временные решения типа «Пока поставим public потом напишем сеттер», а потом сеттер никто не пишет, дабы не переписывать всё приложение… (сеттеры и геттеры в данном контексте равнозначны)
        • +1
          Интересно, как, например, дескрипторы могут заставить «переписывать всё приложение»? Для внешнего кода ничего не изменится.
      • +1
        Ну почему? __getattr__ может использоваться для перехвата определенных атрибутов. Класс может не иметь атрибута, но при обращении к нему атрибут может вычисляться динамически.
        class Test:
        def __getattr__(self,attrname):
        if attrname == «blahblahblah»:
        return «blahblahblah»
        else:
        raise AttributeError, attrname

        t = Test()
        print( t.blahblahblah ) #prints «blahblahblah»

        • 0
          Пардон…
          class Test:
              def __getattr__(self,attrname):
                  if attrname == «blahblahblah»:
                      return «blahblahblah»
                  else:
                      raise AttributeError, attrname
          
          t = Test()
          print( t.blahblahblah ) #prints «blahblahblah»
          </sorce>
          • +1
            А еще, для правильной функциональности deepcopy() в python 2.6 баг, потому не стоит обращаться к self.somedict внутри __getattr__, стоит делать object.__getattribute__(self, key). Такие дела.
        • 0
          Ну, это его каноническое (имхо) назначение в системах типа ORM. Автор имел в виду мне кажется обращение перехват обращения к реальным атрибутам
      • 0
        От себя пример — у вконтакта есть API, там методы называются например wall.GetPost(some_kwargs)
        Я реализую библиотечку для работы с API. Как мне сделать вызов этого метода из моей библиотеки (если не реализовывать все методы самостоятельно а просто проксировать их)?
        вариант 1 (без __getattr__):
        api.call('wall.GetPost', **kwargs)

        и второй вариант (с __getattr__):
        api.wall.GetPost(**kwargs)
        с сохранением «стека»

        По сути функционал не изменился но выглядит нагляднее.
        • 0
          Всякие SOAP-клиенты и прочие RPC так же делают.
  • +2
    «автор читал доки по Python» и как же про filter не знать тогда? ) он веть даже не только в питоне
  • +2
    отступы у вас табами в примерах, не по pep-0008)
    • 0
      и здесь вы со своим PEP-8 =)
      • +10
        В голове мои опилки, не беда, pep-8 не читал я, да да да :)
    • 0
      Заменил табы на пробелы. Правда, привык к ширине табуляции 2, вместо 4 желательных по PEP-8.
      • 0
        Вы представьте если в коде будет много блоков с отступами, например многократное вложение if в функции, которая вложена в другую функцию. Получатся слишком длинные строки, читабельнее 4 пробела.
        • 0
          Слишком длинные у нас получатся :( У автора с думя пробелами на таб будут корче
        • +2
          Значит надо рефакторить код :)
      • –2
        Ну если пишете для себя, то нет проблем, хоть три.
        • –3
          Я так и знал, что никто не поймет подколки: )
    • +3
      PEP-8 не требует пробелы вместо табов, перечитайте его чтобы убедиться. Но, несмотря на это, код в примерах действительно недосчитался кучи пробелов вокруг знаков равенства/неравенства.
      • 0
        Use 4 spaces per indentation level.

        For really old code that you don't want to mess up, you can continue to
        use 8-space tabs.

        For new projects, spaces-only are strongly recommended over tabs.
        • +1
          Ну, да, вот я и говорю не требует стандарт пробелов (всего-лишь рекомендует).
          • +1
            рекомендует, но весьма настоятельно (strongly)
  • +4
    Тут дело даже не в том, что человек пришел с С++, а в том что документация была прочтена поверхностно.
    А когда люди со своими привычками в другом языке лезут в python, вот это действительно выглядит страшно. Например, видел несколько раз, как вчерашние программисты на delphi ставят в python коде в конце каждой строки точку с запятой. По синтаксису это допустимо, но это страшно…
    • +1
      А эти дельфисты документацию вообще читали? Как можно такой момент упустить — ума не приложу. Даже если изучать Python по примерам, надёрганным с интернета, будет очевидно, что точка с запятой не нужна (:
      • +3
        Эта волшебная сила привычки… Вы не представляете, насколько на автомате оно ставится :)
        • +1
          Представляю.
          Но тут еще дело уважения к языку.
          Ведь каждый язык программирования — это свой синтаксис и своя семантика, а отсюда следуют своя терминология, свои «правильные» подходы, своя культура и т.д.
          И оно отнюдь не только для того, чтобы выделиться.

          Во-первых, единство стандарта позволяет всем писать более-менее одинаково (с точки зрения форматирования и использования конструкций языка для каких-либо простых задач типа сортировки).

          Во-вторых, этот единый стиль не взялся с потолка, а длительное время шлифовался профессионалами, специализирующимися на данном языке программирования. Следовательно, большинство вопросов «против», которые вы можете придумать, исходя из того, «как оно сделано в таком-то моем любимом языке программирования», окажутся либо уже давно решенными, либо противоречащими культуре языка (хотя, может быть, работать оно будет корректно).

          Можно привести множество примеров, но как Java-программист замечу: в Java до сих пор нет замыканий )
        • 0
          Действительно, не представляю. Я пишу, в основном, на C++, но при написании скриптов на Python очень редко возникает желание поставить точку с запятой или скобку после if. С привычкой нужно бороться, так как в каждом языке — своя культура оформления кода.
      • –2
        не нужна и мешает — это две большие разницы. Коду на многих языках; между операторами/строками не мешает, но в некоторых одинакова… Вам жалко, что кто напишет в коде что-то что коду не мешает, а мешает лишь вашим привычкам?
        • +4
          Результат проверки: не питонист.
          • –4
            Даже не пртендовал, так что минус уберите :)

            На самом деле пытаюсь понять в чём python «круче» php для веб-приложений, но не получается. Все (на хабре) твердят, что круче, а на практике на django гугл рассылке мну далеко послали… я Понимаю, что gae и php вещи несовместимые (это ж не за кофем ходить), но была бы там минимальная поддержка пхп я б за 10 (ну, может 11) дней написал фреймворк свой (или портировал бы симфони) с учетом их бигтабле и т.п. а из джанги послали, типа нефиг кроссплатформенный фреймворк пытаться запустить на питоновской платформе… пытался примкнуть к опенсорцу фреймворку чисто для гае — тоже послали, типа нефиг идеи рельс на питон тащить, хотя рельс не видел и не слышал, а исходил из впечатлений от php фреймворков… как тут можно быть питонистом? Я ж даже патчи не предлагал со своими asm-c-php-style'ом, а спрашивал «как», «стоит ли так»… :(
            • 0
              В больших сообществах такое случается — идите к нам в Common Lisp — там все дружелюбно и компетентно
              • +2
                Ну ну, на десятом вопросе «а вот в php я делал так», у вас коллективно проснется желание забанить нового участника сообщества навсегда, хорошо если без физических воздействий.
                • 0
                  «Забудьте то чему вас учили в школе» Ⓒ…
            • +2
              Да не пытаетесь вы. Вы берете свои привычки из PHP и тянете их в Python, с минимальной адаптацией, без разбора, что языки разные, очень разные. С точно таким же успехом вы будете писать на LISP как на PHP. Так с чего бы ощутить разницу? Вы пишете все также на PHP, просто вминаете свой PHP в чужеродные конструкции. Так только батхерт можно ощутить, а не преимущества.

              Вам надо переключиться, и врубиться — это не PHP, не JAVA и не С. Это Python/Ruby/PREL/LISP/Erlang/Ocaml/_нужное вписать_. Выкиньте свои привычки, и научитесь новому языку.

              Это не означает, что при написании программ на императивном python вам нельзя использовать немного функционального стиля. Тот же sum() восхитителен. Но если вы начнете писать на LISP в python, вы получите говнокод.
              • 0
                Какие могут быть привычки, которые надо выкидывать? Вот сижу уже минут 10 и думаю, что из моих привычек относится к PHP, а что к программированию/проектированию ПО вообще, в какой момент я начинаю думать категориями языка, а не абстрактных классов/объектов со свойствами/методами. В голову пришло только, что, может быть, не стоит использовать множественное наследование от абстрактных классов для реализации привычных интерфейсов, а держать эти интерфейсы в голове, надеясь, что не забудешь определить какой-то метод.
                • +2
                  Мне выискивать и цитировать все, немного лень. Ну например вас огорчает отсутствие public и private, и вот вы уже хотите использовать всю мощь языка и перекрыть доступ к атрибутам при помощи тяжелой магии. В python так не делают.

                  Потом у вас странная цель сразу написать свой фреймворк, зачем? Изучите вначале хотя бы один, чтобы вообще почувствовать, как это работает в python.

                  В общем один из возможных стартов, прочесть и выполнить упражнения к django, те, что на официальном сайте.
                  • –1
                    Цель была скорее не написать свой фреймворк, а написать полноценное приложение с нуля, без использования компонентов не входящих в стандартную библиотеку. Когда понял, что начинать нужно чуть ли не с написания своего веб-сервера, пришёл к выводу, что надо писать фреймворк или придётся копипастить 90% кода в следующее приложение. В принципе можно это назвать изучением языка «снизу вверх», сначала реализовать самому, чтобы понять как работает, а потом смотреть готовые решения, чтобы понять плюсы и минусы своего и либо допилить его до ума, чтобы было «не хуже как у людей», либо стереть, чтобы случайно не опозориться :) Как-то жизнь научила, что такой способ изучения языка позволяет лучше оценивать плюсы и минусы решений (как своих, так и чужих), чем замена «бизнес-логики» в чужом коде относясь к «инфраструктуре» как к чёрному ящику.
                    • 0
                      self.maluke.com/webdev например. Гуглится легко, было бы желание.
                      • 0
                        Всего и надо было запрос по-русский написать :) А если серьезно, то WebOb я ковырял довольно сильно, начинал переводить документацию к нему, потом понял, что для моих задач он излишне громоздок и начал писать свой кое-какие идеи из него позаимствовав.
        • 0
          Еще я вам кратко ответил, надо разъяснить — понятный код в Python это принцип! Это заповедь программиста.

          Но ведь писать понятный код надо начиная с момента, когда количество участников разработки переваливает за ноль, и стремительно приближается к единице.

          Так вот лишние элементы, не несущие смысловой нагрузки, никак не улучшают восприятие. Когда я смотрю на разряженный код C/C++/Java, я отчетливо ощущаю раздражение от совершенно ненужных {};, за которыми еще и следить приходится.
    • –3
      Тем не менее, точки с запятой желательны после pass и break.
      Не так страшны вчерашние программисты на дельфи, как они же, пишущие код для введения в заблуждение вероятного противника:
      for i in range(100): a += 1; b += a;
      • +11
        > точки с запятой желательны после pass и break.
        Почему это вдруг?
        • –2
          Хм. У меня почему-то было стойкое ощущение, что ставится почти всегда, сейчас поискал примеры кода — в 4 из 5 случаев точки с запятой нет. У меня, насколько помню, эта привычка появилась из-за какой-то IDE, которая не подсвечивала pass, а только pass;
      • 0
        хорошо что я давно забыл что так можно писать :)
        все же, для чего точка с запятой после pass или break?
        • +10
          Похоже, что я был неправ.
  • +2
    >Плоское лучше, чем вложенное

    Этот совет не является питон-специфичным и может быть применён в любом другом языке.
    • 0
      Тем не менее, часто натыкаюсь на

      def getUserData(user):
        if user.authorized:
          user.doAnything()
          # еще куча кода
        else:
           raise UserError('Not authorized')
      • +2
        Конструкция

        if ( открыть_файл ) {
            // ...очень...
            // ...много...
            // ...кода...
        }
        else {
            // сообщить об ошибке
        }

        лично мне встречалась мне как минимум в 4-5 языках. Думаю, со временем их количество будет расти (вместе со списком языков, код на которых мне доведётся читать :))
        • 0
          Я не программист, я только учусь:) Поэтому позвольте задать нубовский вопрос. Как правильно поступить в этом случае? Я вижу только вариант перенести «очень много кода» в отдельную функцию.
          • +6
            if (! открыть файл) {
                // обработать ошибку
            }

            // работаем с открытым файлом
    • 0
      Тут уместно вспомнить про «Совершенный код», да.
  • +1
    Спасибо за статью! Немного от себя:

    На просторах интернета обнаружил «полезнейший» совет: чтобы обращаться к элементам словаря, как к атрибутам класса, то есть, в стиле JS (myobject.myelement), можно воспользоваться таким приемом

    А зачем вообще такое может понадобится? Переделывать синтаксис одного языка под другой? Я понимаю, что кому-то, может быть, так и привычно, но явно не большинству… Да и способ не тот: return self.key естественно не поможет, возможно, скорее что-то вроде return self.__dict__[key].

    А в последнем случае можно также использовать списковые включения, так даже более понятно:
    b = [x for x in a if x >=5]
    

    • 0
      Я тоже не очень понял, в чем плюсы. мало того, что невозможно сделать что-то вроде a[«key_»+s], так еще возникают трудности с обращением к атрибутам.

      Списковые выражения сам с удовольствием использую. Единственное: надо засмечать случаи, когда условие становится чересчур раздутым и лучше уже сделать другим, более очевидным способом.
    • 0
      Реализацию мимикрии словаря под объект предлагаю не обсуждать, она конечно хромая.
      Но сама возможность такой работы со словарем, бывает удобной.

      Например я вижу работу с современными NoSQL БД именно через словари, а не через прибитые гвоздями модели. Но доступ к элементам словаря стандартными языковыми средствами просто ужасен. Если у вас в коде таких обращений будет много, на него будет страшно смотреть.
      • 0
        Это имеет смысл только в таких вот узкоспециализированных задачах, вроде написания ОРМ. Тема мне близка, потому что я писал таким образом свой для MongoDB :)
        А вообще "[]" — вполне стандартный способ, тот же в C++ работает точно так же. Но ничего не мешает даже в том же ОРМе реализовать интерфейс-объект, который будет в конструкторе из какого-нибудь JSON собирать себе поля. Для этого извращаться над самими словарями необязательно.
        • +1
          Ну можете колдовать со сборкой объектов, и дальнейшей их обратной разборкой. В MongoDB словарики, не забыли? Так зачем делать двойную работу? Потому что сто пятьдесят криворучек не смогли корректно написать __getattr__?

          Ну и тут вроде python обсуждается, а не c++, большая концентрация [] на строку вредна для психики.
      • –1
        Для работы с базой удобно использовать namedtuple. Это ещё и эффективнее.

        Менять же синтаксис языка — дурной тон.
        • –1
          Дурной тон, это путать понятия. Синтаксис языка определяет доступ к элементам объекта через точку. Мы не затрагиваем синтаксис, мы используем стандартную, и очень активно используемую, возможность языка, чтобы в рамках его синтаксиса добавить сахара к работе со словарем. И никто не сказал, что это будет простой вызов __getitem__, может там какие-то дополнительные обработки обнаружатся, проверка данных, например.

          То есть реализуется некий класс, поведение которого отличается от обычного словаря, но порядок работы как со словарем нет. То есть где нужен словарь, он остается словарем. И это очень удобно, это экономит время на сборке/разборке объекта.

          — namedtuple достаточно статичная вещь, а документы в MongoDB (в качестве примера) вещь изменчивая. и четкой структуры не имеющая.
          • –1
            Если вы используете словарь, то и работайте с ним как со словарём, не путайте людей, которые позже будут разбираться в вашем коде. Мне тоже не нравиться много пунктуации: квадратных скобок и кавычек при работе со словарями, но я всё-таки до такого не дохожу.

            namedtuple, конечно, более удобен при чтении строк из реляционной БД.
            • –1
              В своей библиотеке я буду работать со словарем так, как мне это кажется правильным. А чтобы не путаться надо хотя бы readme читать.

              Я понимаю, вам кажется, что вы тут боретесь за чистоту языка, но это не так. Никто язык не трогает. Словарь это такой же класс, как и все остальные, от него также можно унаследоваться, и переделать его поведение под себя. Если вам это кажется наглостью, то уберите из stdlib defaultdict. Он тоже трогает святое.
        • –2
          Кстати про namedtuple:

          In [6]: P.__mro__
          Out[6]: (<class '__main__.P'>, <type 'tuple'>, <type 'object'>)

          Сволочи, они снаследовались от кортежа! На костер.

          Я кстати за костер, но по другим причинам — namedtuple это образец кривости, это непередаваемый пи. Ну что стоило принять хотя бы кортеж, а не строку?? И вот вы этим говном пользуется, а значит __getattr__ переопределить это немыслимо?
          • –1
            Вы, похоже, плохо понимаете, что такое namedtuple. Это tuple с доступом через атрибуты к элементам кортежа. И поэтому он унаследован от tuple, имена полей не хранятся в объекте namedtuple как в объекте словаря, они хранятся в классе. Именно поэтому namedtuple эффективен.
            • –1
              Вы похоже не читаете, что комментируете. Беседовать с человеком, который читает в моем тексте то, что он желает, не стоит моего времени. Наша встреча была ошибкой.
      • +1
        Чем ужасен-то? .get(), .setdefault() — прекрасные методы, и их и надо использовать.

        Без квадратных скобок все равно не обойдетесь, если ключ в переменной. Или будете из принципа использовать getattr() / setattr()?
        • 0
          обычно когда записи читаются из базы, то ключи не а переменной, а фиксированы схемой БД. И код действительно выглядит не очень читабельно из-за обилия пунктуации
        • 0
          Визуально ужасен, если много. Сравниваем — Product.id vs Product['id'].

          Get и setdefault отличные методы, и их тоже надо использовать, я с вами абсолютно согласен. И ни в коем случае извращаться с getattr над словарем я не собираюсь. С другой стороны, если кто-то использующий библиотеку, сунет этот класс куда-то, где дернется getattr, то все сработает как ожидалось.

          Но предположим, что некто, крайне пуританских взглядов, решил не мимикрировать словарь под объект, а создать честный объект. Как он будет проходиться по атрибутам своего объекта? Через задний проход, потому что объекты не предназначены для обхода атрибутов. А с нашим словариком он просто сделает for k, v in product.items(). Это если не вспоминать о времени, затрачиваемом на сборку объекта на лету, и его разборку перед упаковкой в БД.

          Итак есть два пути — заточить объект так, чтобы он мимикрировал свое поведение под словарь, или не выпендриваться, и обернуть словарь так как надо, а методы словаря, оптимизированные, написанные на C, достанутся на халяву.

          Напоминаю про namedtuple, которым все пользуются, но который является точно такой же надстройкой над tuple. Ну и кто от его использования пострадал? Ну только настоящие питонисты, которых бесит передача списка полей строкой, получили психологическую травму, увидев такое в stdlib.
  • 0
    Эта статья не только полезна, но и вредна начинающим любителям питона.

    Питон ломает стереотипы статических языков при работе со списками. Мне кажется упор должен быть на это. Функция filter как пример анти-питоновского подхода.
    • 0
      Почему? Вполне себе нормальная функция, всякие map, reduce и иже с ними давно уже есть и в других языках, даже в том же JS, если применить underscore.js. Другое дело, что питон по умолчанию запросто представляет к любому списку интерфейс итератора и простейший синтаксис срезов, вот это большой плюс и удобство. Тот же STL в C++, насколько я знаю, пока такой простоты не позволяет.
      • +1
        filter нормальная функция конечно, но когда фильтрующая функция выражена x >= 5, то кажется более подходящим использовать фильтрацию генератором к примеру. Просто и наглядно. А для filter оставить более сложные фильтры.
      • +5
        reduce в питоне уже ппрактически нету.
        filter и map заменяются list comprehension'ом, который зачастую проще выглядит из-за отсутствия лямбд.
        • +1
          Могу в качестве парирования привести старый добрый «экспертный» подход к алгоритмическому расчету факториала:
          fact = lambda x: reduce(int.__mul__, xrange(2, x + 1), 1)

          Да и вообще, это вы зря, товарищ. Очень полезная вещь в некоторых случаях :) И лямбды тут ни при чем, можно просто передать указатель на функцию, то есть ее имя. Для сложной фильтрации самое оно, как правильно заметили выше.
          • +1
            We already have sum(); I'd happily trade reduce() for product(), so that takes care of the two most common uses.
        • 0
          что значит «уже»? убрали?
          • 0
            www.artima.com/weblogs/viewpost.jsp?thread=98196
            В 3.х reduce убран из builtins в модуль itertools.
            • 0
              Жаль :( мне так питон понравился своей реализацией reduce/// имхо красивше чем в php///
              • 0
                изучение слепой печати на двух раскладках сразу даёт о себе знать
              • +16
                Почти что угодно почти где угодно красивее чем в PHP, это вообще отдельный вопрос.
                • –1
                  просто это чуть ли не единственный плюс питона, который я заметил при попытке с перехода на не го с php скобочки и $ за качественный плюс, имхо, считать нельзя

                  теперь я не смогу убедить даже своих «соратников», не то что заказчиков, что мы туфтой маемся моглибы реализовать проекты быстрее.
                  • +2
                    При переходе к другой парадигме нужно менять мышление. Иного способа быстро и надежно использовать новый инструмент просто не существует.

                    Пример из жизни: насколько быстро человек освоит IDE, если до этого он писал программки в блокноте и собирал из командной строки? Естественно, в первое время ему это будет казаться магией, и захочется продолжать работать с командной строкой, потому что «понятно, как оно работает». Просто сила привычки и недоверие ко всему новому.
                    • 0
                      Не согласен :) работая в npp и idle переход на eclipse был просто сказкой! Процесс ускорился на порядок.
                    • 0
                      Не стоит путать горячее с мягким. Разный масштаб, разный инструмент. Когда-то нужен eclipse, когда-то ipython. Хотя обычно обхожусь vim'ом))
              • 0
                Так а в чем проблема?

                from itertools import reduce

                и можно писать как раньше
  • +2
    1. Зачем вообще обращаться к элементам словаря как к атрибутам класса?
    2. Раз уж python-way, то не
    not " "in name
    , а
    " " not in name
    И скобки вокруг не нужны.
    3. В разделе «Удаление элементов из списка» у вас написано i вместо item.
    4. В 100% случаев вместо filter лучше использовать генераторы списков.
    • –1
      Спасибо за поправки.
      Не в 100%. Если условие имеет смысл вынести в отдельную функцию (не lambda), то кроме filter вариантов не остается.
      • +7
        [x for x in items if func(x)]
        • 0
          filter(func, items) — красивше
          • +1
            А если нужно две функции сразу? А если в функцию еще нужно передать пару параметров? А если полученные элементы нужно еще как-то обработать?

            И если сегодня вы на все эти вопросы отвечаете «нет», то есть ли гарантия, что завтра вам не придется ответить «да»? Может все-таки сразу написать максимально гибко и не менее (а по мне так и более) ясно?
            • –1
              Вам это более ясно потому что вы более привычны к императивному программированию, чем к функциональному.

              А если понадобиться что-то более сложное, то понадобятся листовые выражения или можно написать нужную функцию прямо здесь — это часто чище, чем замудрённое list comprehension.

              Что касается гибкости и красоты — нет ничего лучше перловых map и grep.
              • 0
                что значит написать функцию прямо здесь?
                пишите def на предыдущей строчке
                или прям вот так обязательно в этой же самой строчке?

                P.S.
                я из тех кто считает что конструкция lambda в питоне должна умереть как абсолютно ненужная
                я правда reduce бы оставил, уж очень он привычен мне как человеку пришедшему в питон с хаскеля
                • 0
                  прямо здесь — def в локальном контексте, т.е. внутри текущей функции
              • 0
                Приведите пример, пожалуйста. Все что можно сделать с помощью filter, делается не менее лаконично с помощью генераторов списков.

                При этом код, по мне, более нагляден. Во-первых, мы сразу видим, что возвращается список. Во-вторых, операции наглядно разделены на «что», «где» и «при каком условии». Ну и в-третьих, если почитать документацию Питона, то там везде генераторы списков и нигде filter. :-)
                • 0
                  locale_dirs = filter(os.path.isdir, glob.glob('%s/*' % localedir))

                  читается лучше чем

                  locale_dirs = [f for f in glob.glob('%s/*' % localedir) if os.path.isdir(f)]

                  хотя это дело вкуса, конечно.

                  filter, map и reduce — ближе к ФП, выражения ближе к ИП.
                  • 0
                    ну как сказать
                    Хаскель например чистейший ФЯ и там генераторы списков есть
                    вы из какого ФЯ пришли что вам генераторы не привычны?
  • +7
    У вас ошибки в статье. Во-первых, данный код будет прекрасно работать и будет позволять обращаться ко всем атрибутам словаря.
    class mydict(dict):
        def __getattr__(self, key):
            return self[key]
        def __setattr__(self, key, value)
            self[key] = value
    

    Потому что метод __getattr__ вызывается только тогда, когда атрибут не найден. Для перехвата всех обращений к атрибутам существует метод __getattribute__.

    Во-вторых, неверно, что
    self.key является синтаксическим сахаром для self.__getattr__(key).

    Правильно: getattr(self, key) вызовет self.__getattribute__(key).
    • 0
      Спасибо. Исправил, чтоб не распространять дезинформацию. Действительно, перепроверил изначальный код проекта, ошибки возникали не из-за переопределенного __getattr__().
    • 0
      Взгляд начинающего Python программиста (изучаю для себя)
      1. пример с enumerate и описание range, xrange попадается на глаза даже при беглом просмотре мануала.
      2. обращение к словарю через атрибуты кажется бессмысленным, в свете вышеописанных side эффектов — бессмысленным бредом (комментатору+1)
      3. Мне не нравятся обе конструкции. Или пишите один return в самом конце или группируйте повторяющиеся значения (примерно как в 1 варианте). Я бы оставил единственный return a and b and c and d (вопрос к знатокам в python'е логические операции вычисляются полностью?)
      4. Удаление из списка при проходе специфична в любом языке при чем здесь python? Проще всего пройтись с конца списка и никаких ухищрений. А вообще из предложенного решения не понятно чего же вы хотели?
      — если автор хотел лишь изменить текущий список, для чего писать предлагать создание нового списка с сохранением старого?
      — если автор хотел удалить первые 5 элементов, для чего ему вообще проверки и фильтры? del a[0:5] не вариант?
      — b = [x for x in a if x >=5] скорее больше похоже на python way, но лично мне эта конструкция еще непривычна — может просто неудобно читать справа налево :)
      • 0
        1. и есть в tutorial в разделе Looping Techniques
        Другой вопрос, что кто ж станет читать tutorial.
        3. Нет, не полностью.
        • +1
          Ну… я начинал именно с tutorial'a. Хорошая это привычка начинать с «начала». :))
          • 0
            Значит вы молодец :)
      • 0
        Про 3. Длинные цепочки and очень быстро становятся плохочитабельными. Подход с несколькими if-return в начале, из личного опыта, намного удобнее и понятнее.
      • 0
        > 1. пример с enumerate и описание range, xrange попадается на глаза даже при беглом просмотре мануала.

        Я вот не наткнулся, почему-то, на enumerate() (зело полезная функция, буду юзать), а xrange() вообще еще не было в мануале, когда я язык изучал (лет 10 назад).
      • 0
        > — b = [x for x in a if x >=5] скорее больше похоже на python way, но лично мне эта конструкция еще непривычна — может просто неудобно читать справа налево :)

        Просто функциональщина. Простая и понятная конструкция, нужно просто привыкнуть. Юзаю сплошь и рядом, она гораздо нагляднее, чем цикл.

        P.S. В эрланге, кстати, точно такой же синтаксис у list comprehensors.
  • НЛО прилетело и опубликовало эту надпись здесь
    • НЛО прилетело и опубликовало эту надпись здесь
    • –1
      Если мы уверены, что в списке не будет None, False и пустых строк. К примеру, список имен пользователей.

      def isSomeoneThere():
          return len(activeUsers) and "Yes" or "No"
      
      def isSomeoneThere():
          return any(activeUsers) and "Yes" or "No"
      • 0
        Если нам важно только то, содержит ли список элементы, можно прямо if activeUsers и писать (в приведённом примере — return bool(activeUsers)).
        • 0
          Спасибо, внес в статью правки. В проект тоже =)
          Самое интересное, знал про это и часто писал конструкции вроде
          users = activeUsers or allUsers or "Is there anybody?"


          И все равно проверял непустоту списка с помощью any(a), bool(a) даже в голову не пришел.

      • НЛО прилетело и опубликовало эту надпись здесь
  • +2
    Да, а еще для любителей почитать «доки по стилю» очень рекомендую ссылку google-styleguide.googlecode.com/svn/trunk/pyguide.html. Style guide от Google — это, я вам скажу, не баран чихнул на забор.
    • 0
      Style guide относятся к форматированию кода, не?
      • +1
        Там не только стиль. И про списковые включения там тоже есть.
  • 0
    Удаление элементов из списка. Если список небольшой, то можно вот так:
    for item in a[:]:
        if item<5:
            a.remove(item)
    
    • НЛО прилетело и опубликовало эту надпись здесь
      • 0
        Чисто ради любопытства (на python программирую всего лишь лет пять, поэтому не считаю себя вправе указывать где серебряная пуля, а где нет) — а где в моем фрагменте кода индексы?
        • 0
          Хм, очень странно, что имея пятилетний опыт, вы используете такой неоптимальный подход. У вас будет квадратичная сложность (вместо доступной и очевидной линейной), т. к. на каждой итерации .remove() будет бегать по списку в поисках нужного элемента по значению.
          • 0
            Увы, передо мной редко стоят задачи оптимизации по скорости :(. Я не пишу на питоне игровые движки. Я же написал — для небольших списков :)
    • 0
      так, а чем a[:] отличается от просто a?
      • +1
        Тем, что a[:] создает новый объект. Это аналогично list(a). Кстати, улчше так и писать, чтобы не возникало подобным вопросов.
        • 0
          Идиома a[:] объясняется в tutorial в всего лишь третьем листинге раздела Lists.
          Идиому list(a), где a — тоже list, я вообще впервые вижу.
          • 0
            Тем не менее, человек, который впервые видит python-код, сразу поймет, что list(a) — это создание нового объекта. А вот a[:] — вещь нетривиальная.

            Также, a.reverse() лучше чем a[::-1] по той же причине.
            Правда, для строки a.reverse() не подойдет.
            • 0
              > Тем не менее, человек, который впервые видит python-код, сразу поймет, что list(a) — это создание нового объекта.
              Нет, не поймёт. Начиная с непонятности синтаксиса встроенный_тип() и заканчивая неочевидностью того, создастся ли копия.

              >Также, a.reverse() лучше чем a[::-1] по той же причине.
              a.reverse() меняет in-place, слайс создаёт новый объект.
              • 0
                >>Начиная с непонятности синтаксиса встроенный_тип() и заканчивая неочевидностью того, создастся ли копия.
                Чем такой синтаксис непонятен? Выглядит как обычное преобразование типа.

                >>a.reverse() меняет in-place, слайс создаёт новый объект.
                Неверно написал, имел ввиду a=a[::-1], конечно же.
                • +1
                  > Выглядит как обычное преобразование типа.
                  Вот и чудесно, преобразуем лист в лист и получаем его же.
                  Это не преобразование типа, ага.
                • 0
                  > Неверно написал, имел ввиду a=a[::-1], конечно же.
                  Да, поэтому мы не будем писать a=a[::-1], когда нам надо in-place. А когда нам не надо in-place, мы будем писать a[::-1].
              • +1
                >Нет, не поймёт.

                Я вот понял — функция (если не оговорено иного в сигнатуре вызова) не может возвращать не новый объект, хотя бы потому что он ей не передаётся :)
            • НЛО прилетело и опубликовало эту надпись здесь
              • 0
                Если a нужно в первоначальном виде, то есть функция reversed()
              • 0
                собственно без контекста задачи спор бессмысленный
                кстати а что сделает reversed keyword?
                • 0
                  Вернёт reverse iterator over values of the sequence.
                  • 0
                    И да, это будет не лист :)
                    • 0
                      iterator было достаточно :)
                      • 0
                        help(reversed) в интерпретаторе или pydoc(reversed) в шелле можно было бы и без хабра написать.
                        • 0
                          не всегда там описаны специфичные детали, хотя в общем help прост и доступен
                          • 0
                            Ответ на вопрос «что сделает reversed keyword» там есть.
  • 0
    Как по мне, но большая часть (не пересчитывал, по ощущениям) «косяков» относится к спискам, которые далеко не всем нужны… А насчёт «Плоское лучше, чем вложенное» душа кровью обливается — за «плоское» я получил единственное «хор.» по ИТ связанным специальностям, сделал бы «вложенное» получил бы очередной «отл»!
    • 0
      Но за статью всё равно плюсик, тема полезная, жаль что нев той мере как хотелось бы раскрытая: я сейчас пытаюсь писать на питоне то, что последние 10 лет писал либо на C++ (десктоп, MFC), либо на PHP (веб)… знаю что пишу неправильно, в смысле не python-way, но фидбэка нет и быть не может, поскольку я пытаюсь проникнуться плюсами питона, и перевести, если эти плюсы есть, всех своих «соратников»
    • 0
      Если не списки и словари, то зачем вообще Python?

      > насчёт «Плоское лучше, чем вложенное» душа кровью обливается
      В The Zen of Python есть и более «неудобные» максимы.
      • 0
        Вот я тоже не понимаю… Но все вокруг твердят, что лучше python или ruby (если ограничиваться скриптовыми языками), а я разницы (кроме синтаксиса и yield) почти… Ну, не считая, что php поддерживает реез на уровне языка, а не библиотек/фреймворков
        • 0
          *http
        • +2
          Если не углубляться в холивары:
          -изначальная не-CGI природа Python'а. Соответственно, с одной стороны, огромное количество стабильных и отлаженных средств общего назначения (найдите мне аналог NumPy, SymPy, BioPy для PHP) и большое количество способов реализации сервера (Twisted, Apache/nginx в качестве WSGI-пускалки, gevent как самостоятельный сервер или как WSGI-запускалка, raw sockets) со своими плюсами и минусами (покажите мне аналог Twisted'а для PHP);
          -совершенство языка. Сравните банальные текстовые require/include в PHP и полноценные модули (да, их поведение можно имитировать неймспейсами/объектами, но это всё равно надо писать руками, в питоне же всё будет «из коробки»); плюс всякие приятные плюшки типа list comprehensions, generator expressions, синтаксиса без скобок и т.д.;
          -богатые возможности для построения фреймворков. Если я не ошибаюсь, лучшее, что может тут предложить PHP — «магические» методы у классов. Сравните с наличием метаклассов, декораторов и дескрипторов у Python'а (впрочем, вы сами можете их и не замечать, просто используя фреймворки, но именно они позволяют делать эти фреймворки достаточно удобными);
          -yield гораздо серьёзнее, чем кажется на первый взгляд. Это почти полноценная континуация, соответственно, сам по себе yield позволяет реализовать кооперативную однопоточную многозадачность (см. Twisted'овский @inlineCallbacks) без особой поддержки со стороны интерпретатора, в отличие от приснопамятного node.js, в котором это принципиально невозможно и остаётся только пользоваться голыми коллбеками. (вообще говоря, будь Twisted чуть пошустрее, это была бы однозначно лучшая сетевая библиотека Python'а. но издержки event loop'а на pure python дают о себе знать)
          • 0
            Своего рода аналог Twisted пожалуй PHPDaemon. Хотя Twisted безусловно мощнее.

            Мне, кстати, еще очень нравится в Python множество способов передавать/принимать аргументы в функциях:

            для передачи
            func(arg1, arg2)
            func(arg2=var2, arg1=var1)
            func(*args_tuple)
            func(**args_dict)

            для приема
            def func(arg1, arg2)
            def func(*args)
            def func(**kwargs)

            В PHP есть func_num_args(), func_get_arg(), func_get_args() но это все же не то.

            Декораторы часто бывают полезны.

            Еще классно что можно передать функцию в качестве аргумента другой функции (в PHP с появлением лямбд тоже стало можно, но такая конструкция $func($arg1, $arg2), когда вызывается нечно начинающееся со знака $ немного пугает)

            Ну и yield несомненно очень мощная штука.
            • 0
              > Еще классно что можно передать функцию в качестве аргумента другой функции
              Это банальщина, без которой современный язык достаточно высокого уровня вообще уже не должен существовать.
          • 0
            Спасибо за ответ, есть над чем подумать. Хотя вот кажется, что удобство использования модулей компенсируется неудобством их создания, а может просто руку надо набить, но даже того, что я видел хватило понять, что единообразия не существует, а значит есть плюсы и минусы у каждого подхода по разделению классов на файлы модулей, раз кто-то предпочитает держать весь модуль в __init__.py, кто-то раскидывает «один класс-один файл», кто-то группирует классы по файлам, кто-то комбинирует все три метода, плюс кто-то экспортирует всё, а кто-то прямо перечисляет
            • +1
              Да это в общем-то без разницы. Да, единообразия не существует.
              Наружу всё равно в идеале торчит только то, что в документации описано (всмысле, если можно вызвать что-то ещё, про это знать необязательно).
            • 0
              >удобство использования модулей компенсируется неудобством их создания
              Модуль=файл. Без каких-либо дополнительных синтаксических наворотов. Какое ещё «неудобство создания»?
              >единообразия не существует
              Есть style guidelines. Это примерно как сказать «не существует единообразия в расстановке { }, значит, они плохие».
              >предпочитает держать весь модуль в __init__.py
              Вы явно плохо знакомы с Python'ом.
              >кто-то экспортирует всё, а кто-то прямо перечисляет
              Это исключительно на усмотрение создателя. Это тем более неважно, что «прямое перечисление» определяет только то, что будет импортнуто после import *, что является крайне дурным тоном по меркам питонокоммьюнити.
              • 0
                сорри, имелся в виду пакет и организация модулей-файлов внутри него.
              • 0
                А вообще да, с питоном знаком плохо, иначе бы в этом топике был с другой стороны :)
  • –21
    Прочитал статью и комментарии к ней; в очередной раз убедился в ущербности Python'а.
    • НЛО прилетело и опубликовало эту надпись здесь
      • +2
        А именно в том, что его не могут осилить риальные Це-программисты, понятно же!
    • +1
      без аргументов слив «низащитан»
  • +1
    Если что, len(a) ничего не считает, ее сложность всегда О(1). wiki.python.org/moin/TimeComplexity
    • НЛО прилетело и опубликовало эту надпись здесь
      • 0
        Ссылка вообще исключительно про список говорит.
        • 0
          Хозяйке на заметку.
          len(a) -> O(1) не только для списка, а и для кортежей, словарей, строк, множеств и всяких других унаследованных от с-api типов
          • 0
            UPD. Речь о CPython, как реализовано в Pypy, Unladen Swallow, Jython, Stackless и т.д. — незнаю
    • 0
      Интересно:

      import time
      def test(f, a):
        t=time.clock()
        for i in xrange(1000000):
          f(a)
        return time.clock()-t
        
      a=range(1000000)
      
      print test(len, a)
      print test(bool, a)
      
      len(a) отрабатывает быстрее всех, за ней any, за ней bool. Не ожидал.
      print test(any, a)<source>
      
      • +5
        Для начала — это экономия на спичках, писать лучше как понятнее. Дальше — тест:

        import time
        
        def measure(func):
            def inner():
                arr = range(100000)
                start=time.clock()
                func(arr)
                return time.clock()-start
            return inner
        
        @measure
        def test_len(arr):
            for i in xrange(1000000):
                if len(arr):
                    pass
        
        @measure
        def test_any(arr):
            for i in xrange(1000000):
                if any(arr):
                    pass
        
        @measure
        def test_bool(arr):
            for i in xrange(1000000):
                if bool(arr):
                    pass
        
        @measure
        def test_simple(arr):
            for i in xrange(1000000):
                if arr:
                    pass
        
        print test_len()
        print test_any()
        print test_bool()
        print test_simple()
        
        


        выводит

        0.138603
        0.213345
        0.255277
        0.064151
        
        


        Самый понятный вариант оказался, как это часто бывает, самым быстрым.
        • +2
          Судя по dis.dis() последних двух функций, отличие между ними в том, что if bool(arr) вызывает bool(), а затем выполняет JUMP_IF_FALSE, а if arr: делает сразу JUMP_IF_FALSE, а сама Boolean operation выполняется, видимо, в C.
      • 0
        Для замеров производительности путем многократных повторений есть такой модуль docs.python.org/library/timeit.html
        Есть ли какая-то разница писать замерялку самому или использовать этот модуль не знаю, но если есть — почему не воспользоваться?
  • 0
    чтобы обращаться к элементам словаря, как к атрибутам класса, то есть, в стиле JS (myobject.myelement), можно воспользоваться таким приемом...

    a.1
    #Ошибка! Атрибут не может начинаться с цифры
    

    Вообще-то конструкция a.1 совсем не в стиле js. Также как и в python, в js она выдаст ошибку.
  • +1
    О! Да такое сплошь и рядом! У нас в PHP тоже, когда приходят плохокодеры из Явы, Си или какого-нибудь дотнета (я так догадываюсь, что они приходят с других языков, по стилю их кода), такое творится, что в питоноводам и не снилось, например начинают строки посимвольно циклом разбирать (ага, всего то раз в 1000 медленнее чем в Си) вместо использования встроенных функций, или проверять наличие элемента в большом массиве через in_array() (то есть, не по индексу хеша, а по значениям, что требует полного перебора элементов).

    Ну а программеров с явы легко распознать по излишней любви к созданию классов сотнями, они чуть ли не каждую переменную в свой объект пытаются завернуть, и фабрик вокруг понастроить, производительность естественно плачет и рыдает, посмотрите на какой-нибудь Zend Framework.

    Ну и еще есть тип, видимо приходят с руби или яваскрипта, которые к каждому объекты пристраивают геттеры, сеттеры и систему событий, этих вообще убивать можно сразу.

    И вообще, пытаться в один язык тащить приемы из другого языка, по моему, всегда получается плохо.
  • +5
    Вот еще несколько заметок, как избавиться от С прошлого и начать жить на Питоне:
    1.
    плохо: lst[len(lst) — 1]
    хорошо: lst[-1]

    2.
    плохо: lst[random.randint(0, len(lst) — 1)] #да, и такое встретишь порой
    хорошо: random.choice(lst)

    3. Распаковка tuple: p = (1, 2)
    плохо:
    x = p[0]
    y = p[1]

    хорошо:
    x, y = p

    4. Аналог Си операторам ?:
    a = x if cond else y #введен в Python 2.5
  • 0
    У меня есть вопрос к профи. Как удалить элемент из списка словаря, если его, возможно там нет?
    В PHP, например, можно всегда писать unset($arr['val']) — одна строчка. В Питоне, если элемента не будет, возникнет исключение. Приходится писать:
    if 'val' in arr:
        del arr['val']
    

    Это же не гигиенично.
    • 0
      извините, что встреваю, но может сработать вот так:
      spam['egg'] = None
      

      если элемент по ключу будет не найдет, то он будет создан.
      • 0
        хе, все проще:
        spam.pop('egg')
        
        • 0
          погорячился, pop() вызывает исключение
          • 0
            Ну там же есть необязательный параметр:

          • +4
            пардон, имел ввиду:

            spam.pop('egg', None)

            И никакого исключения (начиная с версии 2.3).
            • 0
              Спасибо.
          • 0
            spam.pop('egg', None)
      • 0
        Надо удалять, а вы создаёте.
        • 0
          а вы знаете как?
          в таком случае и исключения не будет и значение как бы стирается, если дальше будет if arr[key] — то предложенный метод сработает, если if arr.has_key(key), то нет
          • 0
            Нет, не знаю, но передо мной такой проблемы не вставало.

            > в таком случае и исключения не будет и значение как бы стирается
            Очень узкоприменимо.
    • –1
      А создать класс
      class safedict(dict):
        remove = lambda self, key:key in self and (self.__delitem__(key) or True)


      не вариант?
      • +1
        Для того чтобы добавить ненужную и неочевидную функцию в язык, сгородить целый огород? Не стоит этого делать. Помните простые правила. Явное лучше неявного и ошибки никогда не должны замалчиваться. Проверка на наличие нужна и не вредит ни коим образом.
        • 0
          Я абсолютно согласен. Просто предложил решение.
          Конечно же, лучше обрабатывать ошибку или проверять наличие ключа перед удалением.
    • 0
      Ну опять свои правила в чужой монастырь? Эта проверка нужна, это идеология языка, суть которой принцип: явное лучше неявного.
      • 0
        key = func()
        if key in arr:
            del arr[key]
        

        unset(arr[func()]);
        


        Как-то неявно, что явное лучше.
        • 0
          Ок, объясню. Что делает unset в php? Если не ошибаюсь, давным давно в это была функция, которая все время возвращала True, начиная с 4ой версии это вообще оператор языка который в принципе ничего не может возвращать. Итого он всегда уничтожает переменную. Существование самой переменной ему не интересно и вы никогда не узнаете, а был ли мальчик (существовала ли переменная). Пайтон же подобный подход не поощряет, возможно вы указали del неверный объект (банально опечатались), пайтон услужливо скажет вам, что такого объекта нет (хотя опечататься тоже можно по-разному)). А что будет в этом случае в php? Вы можете уничтожить совсем не то, что собирались и в итоге поимеете N времени отладки, чего можно было избежать. Вот поэтому явная проверка лучше.
        • 0
          key in arr and arr.pop(key)

          Явно и легко читается.
          • 0
            А то, что значение выражения будет непонятно какое, это тоже python way?
            • 0
              Написан полный statement, потому какое там значение — никого не интересует. Как и в случае с del и с unset().
  • +1
    def formatName(name):
        if len(name)>=40:
            return False
        if " " not in name:
            return False
        if name[0]=="?"
            return False
    

    Лучше бы (и во всех остальных случаях тоже, а то у Вас где-то есть пробелы в таких местах, где-то нет) так:
    def formatName(name):
        if len(name) >= 40:
            return False
        if " " not in name:
            return False
        if name[0] == "?":
            return False
    

    Я не прав?

    Ещё здесь в предпоследней строке двоеточие забыли.

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.