Переменные внутри и снаружи функции класса __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'
Другие вопросы по тегам