Объявление переменной Python

86

Изучаю Python , и есть некоторые основные сомнения.

1. Я видел объявление переменной (здесь путь) как

class writer:
    path = ""

иногда без явного объявления, но инициализируйте через __init__.

def __init__(self, name):
    self.name = name

Я понимаю цель __init__, но желательно ли объявлять переменную в любых других функциях.

2. Как я могу создать переменную для хранения произвольного типа?

class writer:
    path = "" # string value
    customObj = ??
бср
источник
12
И по поводу второго вопроса: в Python у переменных нет типа, у значений есть. Таким образом, любая переменная может содержать ваши пользовательские объекты.
jpaugh
4
Это поможет, если вы перестанете думать об именах / идентификаторах как о переменных . Это отсылки к объектам
Джон Ла Рой
2
@gnibbler Или имена для них ...
detly
1
@detly, я считаю, что имена - плохой выбор. Вещи обычно имеют только одно имя, тогда как объект может иметь несколько ссылок
Джон Ла Рой
2
@JohnClements, но python не работает как математика. Если вы хотите понять Python, вам нужно знать, что объекты с __name__атрибутом имеют «имя». Не все объекты имеют __name__атрибут, но вы все равно можете ссылаться на них. Если мы начнем называть «ссылку» «именем», мы окажем новичкам медвежью услугу в будущем.
Джон Ла Рой,

Ответы:

202

Хорошо, обо всем по порядку.

В Python нет таких вещей, как «объявление переменной» или «инициализация переменной».

Это просто то, что мы называем «присваиванием», но, вероятно, следует называть просто «именование».

Присваивание означает, что «это имя в левой части теперь относится к результату оценки правой части, независимо от того, на что оно ссылалось раньше (если что-либо)».

foo = 'bar' # the name 'foo' is now a name for the string 'bar'
foo = 2 * 3 # the name 'foo' stops being a name for the string 'bar',
# and starts being a name for the integer 6, resulting from the multiplication

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

Второй момент: когда дело доходит до классов, Python следует очень простому правилу, которое на самом деле гораздо более согласованно, чем то, что делают такие языки, как Java, C ++ и C #: все, что объявлено внутри classблока, является частью класса . Итак, defнаписанные здесь функции ( ) являются методами, то есть частью объекта класса (не сохраняются для каждого экземпляра), как в Java, C ++ и C #; но другие имена здесь также являются частью класса. Опять же, имена - это просто имена, и они не имеют связанных типов, а функции также являются объектами в Python. Таким образом:

class Example:
    data = 42
    def method(self): pass

В Python классы тоже являются объектами .

Итак, теперь мы создали объект с именем Example, который представляет класс всех вещей, которые есть Example. Этот объект имеет два атрибута, задаваемых пользователем (в C ++ - «члены»; в C # - «поля или свойства или методы»; в Java - «поля или методы»). Один из них назван data, и в нем хранится целочисленное значение 42. Другой назван method, и он хранит объект функции. (Есть еще несколько атрибутов, которые Python добавляет автоматически.)

Однако эти атрибуты по-прежнему не являются частью объекта. По сути, объект - это просто набор других имен (имен атрибутов), пока вы не перейдете к вещам, которые больше нельзя разделить. Таким образом, значения могут совместно использоваться разными экземплярами класса или даже между объектами разных классов, если вы намеренно это настроили.

Создадим экземпляр:

x = Example()

Теперь у нас есть отдельный объект с именем x, который является экземпляром Example. На самом деле dataи methodне являются частью объекта, но мы все равно можем искать их с помощью xнекоторой магии, которую Python делает за кулисами. В methodчастности, когда мы смотрим вверх , мы вместо этого получим «связанный метод» (когда мы его вызываем, он xавтоматически передается в качестве selfпараметра, чего не может произойти, если мы посмотрим Example.methodпрямо вверх ).

Что происходит, когда мы пытаемся использовать x.data?

Когда мы исследуем его, он сначала ищет объект. Если его нет в объекте, Python просматривает класс.

Однако, когда мы назначаем x.data , Python создаст атрибут для объекта. Он не заменит атрибут класса.

Это позволяет нам выполнять инициализацию объекта . Python автоматически вызовет метод класса для __init__новых экземпляров при их создании, если они есть. В этом методе мы можем просто назначить атрибуты, чтобы установить начальные значения для этого атрибута для каждого объекта:

class Example:
    name = "Ignored"
    def __init__(self, name):
        self.name = name
    # rest as before

Теперь мы должны указать a nameпри создании Example, и каждый экземпляр имеет свой собственный name. Python будет игнорировать атрибут класса Example.nameвсякий раз, когда мы ищем .nameэкземпляр экземпляра, потому что атрибут экземпляра будет найден первым.

И последнее предостережение: модификация (мутация) и присвоение - разные вещи!

В Python строки неизменяемы. Их нельзя изменить. Когда вы это сделаете:

a = 'hi '
b = a
a += 'mom'

Вы не меняете исходную строку «привет». В Python это невозможно. Вместо этого вы создаете новую строку 'hi mom'и заставляете aперестать быть именем 'hi ', а 'hi mom'вместо этого начинаете быть именем . Мы также создали bимя 'hi ', и после повторного применения aимя bвсе еще остается именем 'hi ', потому что 'hi 'все еще существует и не было изменено.

Но списки можно изменить:

a = [1, 2, 3]
b = a
a += [4]

Теперь bэто [1, 2, 3, 4], потому что мы создали bимя для той же вещи, что и aназвание, а затем мы изменили это. Мы не создавали новый список для aимени, потому что Python просто по- +=другому относится к спискам.

Это важно для объектов, потому что если бы у вас был список как атрибут класса и вы использовали экземпляр для изменения списка, то изменение было бы «видно» во всех других экземплярах. Это потому, что (а) данные на самом деле являются частью объекта класса, а не объекта-экземпляра; (b) поскольку вы изменяли список, а не выполняли простое присвоение, вы не создали новый атрибут экземпляра, скрывающий атрибут класса.

Карл Кнехтель
источник
12
Вот почему мы называем их «идентификаторами» . :-)
Мартейн Питерс
@Karl_Knechtel в вашем примере строки, в конце концов, что произойдет с 'hi', если мы никогда не назовем его 'b'? Это утечка памяти?
heretoinfinity
2
@heretoinfinity: нет; Python использует сборку мусора для освобождения недоступных объектов
Джон Клементс,
Объявление переменных подробно описано в этом видео: youtube.com/watch?v=ao2N37E-D9s .
Ishaq Khan
посмотрите это видео, чтобы получить ответ youtube.com/…
Thao N
23

Это может быть на 6 лет позже, но в Python 3.5 и выше вы объявляете тип переменной следующим образом:

variable_name: type_name

или это:

variable_name # type: shinyType

Итак, в вашем случае (если у вас определен CustomObjectкласс) вы можете:

customObj: CustomObject

См. То или это для получения дополнительной информации.

О. Ароэсти
источник
22

В Python нет необходимости объявлять новые переменные. Если мы говорим о переменных в функциях или модулях, объявление не требуется. Просто присвоить значение имени , где вам это нужно: mymagic = "Magic". Переменные в Python могут содержать значения любого типа, и вы не можете ограничить это.

Однако ваш вопрос конкретно касается классов, объектов и переменных экземпляра. Идиоматический способ создания переменных экземпляра заключается в __init__методе и нигде больше - хотя вы можете создавать новые переменные экземпляра в других методах или даже в несвязанном коде, это просто плохая идея. Из-за этого будет сложно обдумывать или поддерживать ваш код.

Так например:

class Thing(object):

    def __init__(self, magic):
        self.magic = magic

Легко. Теперь у экземпляров этого класса есть magicатрибут:

thingo = Thing("More magic")
# thingo.magic is now "More magic"

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

class Thing(object):

    magic = "Magic"

    def __init__(self):
        pass

А теперь попробуйте:

thingo = Thing()
Thing.magic = 1
# thingo.magic is now 1

Или:

class Thing(object):

    magic = ["More", "magic"]

    def __init__(self):
        pass

thing1 = Thing()
thing2 = Thing()
thing1.magic.append("here")
# thing1.magic AND thing2.magic is now ["More", "magic", "here"]

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

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

детально
источник
12

Для определения объема я использую:

custom_object = None
рыжий
источник
1

У переменных есть область видимости, поэтому да, уместно иметь переменные, специфичные для вашей функции. Вы не всегда должны четко давать их определение; обычно их можно просто использовать. Только если вы хотите сделать что-то конкретное для типа переменной, например, добавить для списка, вам нужно определить их, прежде чем вы начнете их использовать. Типичный пример этого.

list = []
for i in stuff:
  list.append(i)

Кстати, это не совсем хороший способ настроить список. Лучше было бы сказать:

list = [i for i in stuff] # list comprehension

... но я отвлекся.

Ваш другой вопрос. Настраиваемый объект должен быть самим классом.

class CustomObject(): # always capitalize the class name...this is not syntax, just style.
  pass
customObj = CustomObject()
ChipJust
источник