Список умножения и общие ссылки

Рассмотрим случай создания структуры вложенного списка путем умножения:

 li = [[]] * 3
print(li)
# Out: [[], [], []]

 

На первый взгляд нам кажется, что у нас есть список, содержащий 3 разных вложенных списка. Давайте попробуем добавить 1 к первому:

 li[0].append(1)
print(li)
# Out: [[1], [1], [1]]

 

1 получил добавляется ко всем спискам в li .

Причина заключается в том, что [[]] * 3 не создает list из 3 -х различных list с. Скорее всего , он создает list держит 3 ссылки на тот же list объектов. Таким образом , когда мы добавляем к li[0] изменение видна во всех суб-элементов li . Это эквивалентно:

 li = []
element = [[]]
li = element + element + element
print(li)
# Out: [[], [], []]
element.append(1)
print(li)
# Out: [[1], [1], [1]]

 

Это может быть дополнительно подтверждено , если мы печатаем адрес памяти содержащегося list с помощью id :

 li = [[]] * 3
print([id(inner_list) for inner_list in li])
# Out: [6830760, 6830760, 6830760]

 

Решение состоит в том, чтобы создать внутренние списки с помощью цикла:

 li = [[] for _ in range(3)]

 

Вместо того , чтобы создать единый list , а затем сделать 3 ссылки на него, мы теперь создать 3 различные различные списки. Это, опять - таки, может быть проверено с помощью id функции:

 print([id(inner_list) for inner_list in li])
# Out: [6331048, 6331528, 6331488]

 

Вы также можете сделать это. Это вызывает новый пустой список , который будет создан в каждом append вызова.

 >>> li = []
>>> li.append([])
>>> li.append([])
>>> li.append([])
>>> for k in li: print(id(k))
... 
4315469256
4315564552
4315564808

 

Не используйте индекс для зацикливания последовательности.

Не рекомендуется :

 for i in range(len(tab)):
    print(tab[i])

 

Есть:

 for elem in tab:
    print(elem)

 

for автоматизируют большинство операций итерации для вас.

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

 for i, elem in enumerate(tab):
     print((i, elem))

 

Будьте внимательны при использовании «==» для проверки на True или False

 if (var == True):
    # this will execute if var is True or 1, 1.0, 1L

if (var != True):
    # this will execute if var is neither True nor 1

if (var == False):
    # this will execute if var is False or 0 (or 0.0, 0L, 0j)

if (var == None):
    # only execute if var is None

if var:
    # execute if var is a non-empty string/list/dictionary/tuple, non-0, etc

if not var:
    # execute if var is "", {}, [], (), 0, None, etc.

if var is True:
    # only execute if var is boolean True, not 1

if var is False:
    # only execute if var is boolean False, not 0

if var is None:
    # same as var == None

 

Не проверяйте, можете ли вы, просто сделайте это и исправьте ошибку

Питонисты обычно говорят: «Проще просить прощения, чем разрешения».

Не рекомендуется :

 if os.path.isfile(file_path):
    file = open(file_path)
else:
    # do something

 

Делать:

 try:
    file = open(file_path)
except OSError as e:
    # do something

 

Или еще лучше с Python 2.6+ :

 with open(file_path) as file:

 

Это намного лучше, потому что это гораздо более общий характер. Вы можете применить try/except за try/except почти ничего. Вам не нужно заботиться о том, что делать, чтобы предотвратить это, просто заботьтесь об ошибке, которой вы рискуете.

Не проверять против типа

Python динамически типизирован, поэтому проверка типа приводит к потере гибкости. Вместо этого используйте утка ввод проверяя поведение. Если вы ожидаете строку в функции, а затем использовать str() , чтобы преобразовать любой объект в строку. Если вы ожидаете список, используйте list() , чтобы преобразовать любую итерацию в список.

Не рекомендуется :

 def foo(name):
    if isinstance(name, str):
        print(name.lower())

def bar(listing):
    if isinstance(listing, list):
        listing.extend((1, 2, 3))
        return ", ".join(listing)

 

Делать:

 def foo(name) :
    print(str(name).lower())

def bar(listing) :
    l = list(listing)
    l.extend((1, 2, 3))
    return ", ".join(l)

 

Используя последний путь, foo будет принимать любой объект. bar принимает строки, кортежи, наборы, списки и многие другие. Дешевые СУХОЙ.

Не смешивайте пробелы и табуляции

Использование объекта в качестве первого родителя

Это сложно, но это будет кусать вас по мере роста вашей программы. Есть старые и новые классы в Python 2.x . Старые, ну, старые. Им не хватает некоторых функций, и они могут вести себя неловко с наследованием. Чтобы быть пригодным для использования, любой ваш класс должен иметь «новый стиль». Чтобы сделать это, сделать его наследовать от object .

Не рекомендуется :

 class Father:
    pass

class Child(Father):
    pass

 

Делать:

 class Father(object):
    pass


class Child(Father):
    pass

 

В Python 3.x все классы новый стиль , так что вам не нужно делать это.

Не инициализировать класс атрибутов за пределами метода инициализации

Люди из других языков находят это заманчивым, потому что это то, что вы делаете на Java или PHP. Вы пишете имя класса, затем перечисляете свои атрибуты и даете им значение по умолчанию. Кажется, это работает в Python, однако, это не работает так, как вы думаете. При этом будут настроены атрибуты класса (статические атрибуты), а затем, когда вы попытаетесь получить атрибут объекта, он даст вам его значение, если оно не пустое. В этом случае он вернет атрибуты класса. Это подразумевает две большие опасности:

  • Если атрибут класса изменен, то начальное значение изменяется.

Не (если только вы не хотите статического):

 class Car(object):
    color = "red"
    wheels = [Wheel(), Wheel(), Wheel(), Wheel()]

 

Делать :

 class Car(object):
    def __init__(self):
        self.color = "red"
        self.wheels = [Wheel(), Wheel(), Wheel(), Wheel()]