
Всем доброго времени суток! Новогодние праздники позади, все хорошо отдохнули. Тем не мнеее, даже в праздники иногда появляется работа. Мне, к примеру, довелось покопаться в чужом коде на 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 с удовольствием. И помните: особые случаи не настолько особые, чтобы нарушать правила.
