Декларація змінних Python


85

Вивчаючи Python , є основні сумніви.

1.Я бачив оголошення змінної (шлях тут) як

class writer:
    path = ""

іноді, немає явного оголошення, але ініціалізується через __init__.

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

Я розумію мету __init__, але чи доцільно оголошувати змінну в будь-яких інших функціях.

2.Як я можу створити змінну для зберігання власного типу?

class writer:
    path = "" # string value
    customObj = ??

12
І, що стосується 2-го питання: у Python змінні не мають типу: значення мають. Отже, будь-яка змінна може містити ваші власні об’єкти.
jpaugh

4
Це допоможе, якщо ви перестанете думати про імена / ідентифікатори як про змінні . Це посилання на предмети
Джон Ла Рой,

2
@gnibbler Або імена для них ...
detly

1
@tetly, я думаю, що імена - це поганий вибір. Речі зазвичай мають лише одне ім’я, тоді як об’єкт може мати кілька посилань
Джон Ла Рой,

2
@JohnClements, але python не працює як математика. Якщо ви хочете зрозуміти python, вам слід знати, що об'єкти з __name__атрибутом мають "ім'я". Не всі об'єкти мають __name__атрибут, але ви все одно можете мати посилання на них. Якщо ми починаємо називати "посилання" "ім'ям", ми починаємо робити погану послугу для початківців.
John La Rooy

Відповіді:


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, який представляє клас усіх речей, які є Examples. Цей об'єкт має два атрибути, надані користувачем (у 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

Тепер ми повинні вказати, nameколи ми створюємо Example, і кожен екземпляр має своє name. Python буде ігнорувати атрибут class 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 просто по- +=різному трактує списки.

Це має значення для об’єктів, оскільки якби у вас був список як атрибут класу та ви використовували екземпляр для модифікації списку, тоді зміна буде «видно» у всіх інших випадках. Це пов’язано з тим, що (а) дані насправді є частиною об’єкта класу, а не будь-яким об’єктом екземпляра; (б) оскільки ви модифікували список і не виконували просте призначення, ви не створили новий атрибут екземпляра, що приховує атрибут класу.


12
Ось чому ми називаємо їх "ідентифікаторами" . :-)
Мартін Пітерс

Зрештою, @Karl_Knechtel у вашому прикладі рядка, що трапляється з "привіт", якщо ми ніколи не називаємо його як "b"? Це витік пам'яті?
нескінченність

2
@heretoinfinity: ні; Python використовує збір сміття для звільнення недосяжних об'єктів
Джон Клементс

Декларація змінної добре пояснена у цьому відео: youtube.com/watch?v=ao2N37E-D9s .
Ісхак-хан

перегляньте це відео для відповіді youtube.com/…
Тао 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 має (a) ініціалізувати атрибути об’єкта у вашому __init__методі та (b) документувати поведінку вашого класу за необхідності. Вам не потрібно впадати у проблему повномасштабної документації на рівні Сфінкса для всього, що ви коли-небудь писали, але принаймні деякі коментарі щодо будь-яких деталей, які можуть знадобитися вам або комусь іншому, щоб їх взяти.



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()
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.