Переменные внутри и снаружи функции класса __init__()
Я пытаюсь понять, есть ли разница между этими классами, кроме названия? Имеет ли какое-то значение, если я использую или не использую функцию __init__() при объявлении переменной "value"?
class WithClass ():
def __init__(self):
self.value = "Bob"
def my_func(self):
print(self.value)
class WithoutClass ():
value = "Bob"
def my_func(self):
print(self.value)
Мое главное беспокойство заключается в том, что я буду использовать его одним способом, когда это вызовет у меня проблемы в будущем (в настоящее время я использую вызов init).
12 ответов
Переменная установлена снаружи __init__
принадлежат к классу. Их разделяют все экземпляры.
Переменные, созданные внутри __init__
(и все другие функции метода) с предваряющим self.
принадлежат объекту экземпляра.
Без себя
Создайте несколько объектов:
class foo(object):
x = 'original class'
c1, c2 = foo(), foo()
Я могу изменить экземпляр c1, и это не повлияет на экземпляр c2:
c1.x = 'changed instance'
c2.x
>>> 'original class'
Но если я изменю класс foo, все экземпляры этого класса также будут изменены:
foo.x = 'changed class'
c2.x
>>> 'changed class'
Пожалуйста, обратите внимание, как Python scoping работает здесь:
c1.x
>>> 'changed instance'
С собой
Изменение класса не влияет на экземпляры:
class foo(object):
def __init__(self):
self.x = 'original self'
c1 = foo()
foo.x = 'changed class'
c1.x
>>> 'original self'
Я хотел бы добавить что-то к ответам, которые я прочитал в этой теме и этой теме (которая ссылается на эту).
Отказ от ответственности: эти замечания взяты из экспериментов, которые я провел
Переменные снаружи__init__
:
Фактически это статические переменные класса, и поэтому они доступны для всех экземпляров класса.
Переменные внутри__init__
:
Значение этих переменных экземпляра доступно только для имеющегося экземпляра (через self
ссылка)
Мой вклад:
Одна вещь, которую программисты должны учитывать при использовании переменных статического класса, это то, что они могут быть затенены переменными экземпляра (если вы обращаетесь к переменным статического класса через self
ссылка).
Пояснение:
Ранее я думал, что оба способа объявления переменных были абсолютно одинаковыми (глупый я), и это было отчасти потому, что я мог получить доступ к обоим видам переменных через self
ссылка. Именно сейчас, когда я столкнулся с проблемой, я исследовал эту тему и прояснил ее.
Проблема с доступом к статическим переменным класса через self
ссылка заключается в том, что он ссылается на переменную статического класса только в том случае, если нет переменной экземпляра с тем же именем, и, что еще хуже, пытается переопределить переменную статического класса через self
ссылка не работает, потому что создается переменная экземпляра, которая затем затеняет ранее доступную статическую переменную класса.
Чтобы обойти эту проблему, вы всегда должны ссылаться на статические переменные класса через имя класса.
Пример:
#!/usr/bin/env python
class Foo:
static_var = 'every instance has access'
def __init__(self,name):
self.instance_var = 'I am ' % name
def printAll(self):
print 'self.instance_var = %s' % self.instance_var
print 'self.static_var = %s' % self.static_var
print 'Foo.static_var = %s' % Foo.static_var
f1 = Foo('f1')
f1.printAll()
f1.static_var = 'Shadowing static_var'
f1.printAll()
f2 = Foo('f2')
f2.printAll()
Foo.static_var = 'modified class'
f1.printAll()
f2.printAll()
Выход:
self.instance_var = I am f1
self.static_var = every instance has access
Foo.static_var = every instance has access
self.instance_var = I am f1
self.static_var = Shadowing static_var
Foo.static_var = every instance has access
self.instance_var = I am f2
self.static_var = every instance has access
Foo.static_var = every instance has access
self.instance_var = I am f1
self.static_var = Shadowing static_var
Foo.static_var = modified class
self.instance_var = I am f2
self.static_var = modified class
Foo.static_var = modified class
Надеюсь это кому-нибудь пригодится
В дополнение к ответу С.Лотта переменные класса передаются новому метаклассу и могут быть доступны через словарь при определении метакласса. Таким образом, переменные класса могут быть доступны даже до того, как классы созданы и созданы.
например:
class meta(type):
def __new__(cls,name,bases,dicto):
# two chars missing in original of next line ...
if dicto['class_var'] == 'A':
print 'There'
class proxyclass(object):
class_var = 'A'
__metaclass__ = meta
...
...
class User(object):
email = 'none'
firstname = 'none'
lastname = 'none'
def __init__(self, email=None, firstname=None, lastname=None):
self.email = email
self.firstname = firstname
self.lastname = lastname
@classmethod
def print_var(cls, obj):
print ("obj.email obj.firstname obj.lastname")
print(obj.email, obj.firstname, obj.lastname)
print("cls.email cls.firstname cls.lastname")
print(cls.email, cls.firstname, cls.lastname)
u1 = User(email='abc@xyz', firstname='first', lastname='last')
User.print_var(u1)
В приведенном выше коде класс User имеет 3 глобальные переменные, каждая со значением "none". u1 - объект, созданный путем создания экземпляра этого класса. Метод print_var печатает значение переменных класса класса User и объектных переменных объекта u1. В выводе, показанном ниже, каждая из переменных класса User.email
, User.firstname
а также User.lastname
имеет значение 'none'
в то время как переменные объекта u1.email
, u1.firstname
а также u1.lastname
иметь значения 'abc@xyz'
, 'first'
а также 'last'
,
obj.email obj.firstname obj.lastname
('abc@xyz', 'first', 'last')
cls.email cls.firstname cls.lastname
('none', 'none', 'none')
В Python класс поставляется с функциями - членами (методы), переменные класса, атрибуты / переменные экземпляра (и, возможно, методов класса тоже):
class Employee:
# Class Variable
company = "mycompany.com"
def __init__(self, first_name, last_name, position):
# Instance Variables
self._first_name = first_name
self._last_name = last_name
self._position = position
# Member function
def get_full_name(self):
return f"{self._first_name} {self._last_name}"
Создавая экземпляр объекта
my_employee = Employee("John", "Wood", "Software Engineer")
мы по сути запускаем __init__
который будет инициализировать переменные экземпляра вновь созданногоEmployee
. Это значит, что_first_name
, _last_name
а также _position
являются явными параметрами конкретного my_employee
экземпляр.
Точно так же функции-члены возвращают информацию или изменяют состояние определенного экземпляра.
Теперь любая переменная, определенная вне конструктора __init__
считаются переменными класса. Эти переменные используются всеми экземплярами класса.
john = Employee("John", "Wood", "Software Engineer")
bob = Employee("Bob", "Smith", "DevOps Engineer0")
print(john.get_full_name())
print(bob.get_full_name())
print(john.company)
print(bob.company)
>>> John Wood
>>> Bob Smith
>>> mycompany.com
>>> mycompany.com
Вы также можете использовать методы класса, чтобы изменить переменную класса для всех экземпляров класса. Например:
@classmethod
def change_my_companys_name(cls, name):
cls.company = name
и сейчас change_my_companys_name()
bob.change_my_companys_name("mynewcompany.com")
повлияет на все экземпляры класса Employee
:
print(bob.company)
print(john.company)
>>> mynewcompany.com
>>> mynewcompany.com
Как отмечает С.Лотт,
Переменная, установленная вне init, принадлежит классу. Их разделяют все экземпляры.
Переменные, созданные внутри init (и всех других функций метода) и начинающиеся с self. принадлежат экземпляру объекта.
Однако обратите внимание, что переменные класса могут быть доступны через self.<var> до тех пор, пока они не будут замаскированы объектной переменной с аналогичным именем. Это означает, что чтение self. <var> перед присвоением ему значения вернет значение Class. <Var > но потом вернет obj. <var>. Вот пример
In [20]: class MyClass:
...: elem = 123
...:
...: def update(self,i):
...: self.elem=i
...: def print(self):
...: print (MyClass.elem, self.elem)
...:
...: c1 = MyClass()
...: c2 = MyClass()
...: c1.print()
...: c2.print()
123 123
123 123
In [21]: c1.update(1)
...: c2.update(42)
...: c1.print()
...: c2.print()
123 1
123 42
In [22]: MyClass.elem=22
...: c1.print()
...: c2.print()
22 1
22 42
Второе примечание : рассмотрите слоты . Они могут предложить лучший способ реализации объектных переменных.
Пример кода:
class inside:
def __init__(self):
self.l = []
def insert(self, element):
self.l.append(element)
class outside:
l = [] # static variable - the same for all instances
def insert(self, element):
self.l.append(element)
def main():
x = inside()
x.insert(8)
print(x.l) # [8]
y = inside()
print(y.l) # []
# ----------------------------
x = outside()
x.insert(8)
print(x.l) # [8]
y = outside()
print(y.l) # [8] # here is the difference
if __name__ == '__main__':
main()
class foo(object):
mStatic = 12
def __init__(self):
self.x = "OBj"
Учитывая, что foo вообще не имеет доступа к x (ФАКТ)
теперь конфликт заключается в доступе к mStatic экземпляром или непосредственно классом.
подумайте об этом с точки зрения управления памятью Python:
12 значение находится в памяти и имя mStatic (доступное из класса)
указывает на это.
c1, c2 = foo(), foo()
эта строка создает два экземпляра, которые включают имя mStatic, указывающее на значение 12 (до сих пор) .
foo.mStatic = 99
это делает mStatic именем, указывающим на новое место в памяти, внутри которого находится значение 99.
и поскольку (младенцы) c1, c2 все еще следуют (daddy) foo, они имеют то же имя (c1.mStatic и c2.mStatic), указывающее на одно и то же новое значение.
но когда каждый ребенок решает гулять один, все меняется:
c1.mStatic ="c1 Control"
c2.mStatic ="c2 Control"
отныне и позже каждый член этого семейства (c1,c2,foo) имеет mStatica, указывающий на другое значение.
[Пожалуйста, попробуйте использовать функцию id() для всех (c1,c2,foo) в разных состояниях, о которых мы говорили, я думаю, это улучшит ситуацию]
и так устроена наша настоящая жизнь. сыновья наследуют некоторые верования от своего отца, и эти верования все еще идентичны верованиям отца, пока сыновья не решат изменить их.
Надеюсь, это поможет
Попробуйте это и проверьте разницу
class test:
f = 3
def __init__(s, f):
s.__class__.f = f
s.f = s.__class__.f
print(f'def __init__(s, {f})')
print(f's.__class__.f = {f}')
print(f's.f={s.__class__.f}')
print(f'f={f}')
print('===============init over===========')
def setinstancetoOne(s, f):
print(f'def setinstancetoOne(s, {f})')
s.f = f
print(f'class var f = {f}')
def useClassname(test):
print(f'>>>>def useClassname({test})')
print(f'test.f {test.f}')
def p_method(s):
print(f'>>>>def p_method({s})')
print(f's.f {s.f}')
print(f'test.f {test.f}')
print(f's.__class__.f {s.__class__.f}')
print(f'class var f={f}')
# test.__init__.f = 19
t = test(2)
t.useClassname()
t.p_method()
print(f'Outside class t.f {t.f}')
print(f'Outside class test.f {test.f}')
print('______difference__________')
t = test(2)
t.setinstancetoOne(1)
t.useClass()
t.p_method()
print(f'Outside class instance variable(2) {t.f}')
print(f'Outside class class variable(3) {test.f}')
Это очень легко понять, если вы отслеживаете словари классов и экземпляров.
class C:
one = 42
def __init__(self,val):
self.two=val
ci=C(50)
print(ci.__dict__)
print(C.__dict__)
Результат будет такой:
{'two': 50}
{'__module__': '__main__', 'one': 42, '__init__': <function C.__init__ at 0x00000213069BF6A8>, '__dict__': <attribute '__dict__' of 'C' objects>, '__weakref__': <attribute '__weakref__' of 'C' objects>, '__doc__': None}
Обратите внимание, что здесь я установил полные результаты, но важно, чтобы экземпляр ci
дикт будет просто {'two': 50}
, а словарь классов будет иметь 'one': 42
пара ключ-значение внутри.
Это все, что вам следует знать об этих переменных.
Классы подобны чертежам для создания объектов. Сделаем метафору постройки дома. У вас есть план дома, поэтому вы можете построить дом. Вы можете построить столько домов, сколько позволяют ваши ресурсы.
В этой метафоре план - это класс, а дом - это экземпляр класса, создающий объект.
У домов есть общие атрибуты, такие как крыша, гостиная и т. Д. Это то, к чему вы приступаете. Он создает объект (дом) с нужными вам атрибутами.
Предположим, у вас есть:
`class house:`
`roof = True`
`def __init__(self, color):`
`self.wallcolor = color`
>> create little goldlock's house:
>> goldlock = house() #() invoke's class house, not function
>> goldlock.roof
>> True
all house's have roofs, now let's define goldlock's wall color to white:
>> goldlock.wallcolor = 'white'
>>goldlock.wallcolor
>> 'white'