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