Pull to refresh

Python-way. Работа над ошибками

Python *
Sandbox

Всем доброго времени суток! Новогодние праздники позади, все хорошо отдохнули. Тем не мнеее, даже в праздники иногда появляется работа. Мне, к примеру, довелось покопаться в чужом коде на 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 с удовольствием. И помните: особые случаи не настолько особые, чтобы нарушать правила.
Tags:
Hubs:
Total votes 108: ↑98 and ↓10 +88
Views 17K
Comments Comments 192