__init__() 内部和外部变量之间的差异

Posted

技术标签:

【中文标题】__init__() 内部和外部变量之间的差异【英文标题】:difference between variables inside and outside of __init__() 【发布时间】:2010-12-04 23:08:58 【问题描述】:

除了名称之外,这些类之间有什么区别吗?

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__ 方法来声明变量value,会有什么不同吗?

我主要担心的是我会以一种方式使用它,而这会给我带来更多问题。

【问题讨论】:

重复问题中示例的详细答案:***.com/a/9056994/911945 【参考方案1】:

__init__ 之外的变量集属于该类。它们由所有实例共享。

__init__(和所有其他方法函数)中创建并以self. 开头的变量属于对象实例。

【讨论】:

这不是 python 为我做的。如果您不在__init__() 中创建列表/字典/等,它们将在所有实例之间共享。 @too much php:类方法中的所有变量(不考虑可变性——列表和字典都是可变的)都是共享的。对于不可变对象,共享并不有趣。对于可变对象(列表和字典),共享非常重要。 我怀疑可能是这种情况,但我认为如果我陈述我的假设可能会分散问题本身的注意力,为澄清它而欢呼:) 我认为这就是正在发生的事情,但我并不清楚。感谢您清理它。 这有点误导。 init 之外的varnamevariable 集确实属于该类,并且可以通过self.varname 读取,从而为所有实例生成相同的值。但是,当通过实例引用(如self.varname = X)分配值时,new self.varname 将创建仅为该实例,从而隐藏类变量。类 var 仍然可以通过类引用访问(例如:WithClass.varname)。类变量也可以通过任何方法设置,方法是使用类名 (WithClass.myvar = X) 而不是实例引用 (self.myvar = X)。【参考方案2】:

根据 S.Lott 的回复,类变量被传递给元类 new 方法,并且可以在定义元类时通过字典访问。因此,甚至可以在创建和实例化类之前访问类变量。

例如:

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
      ...
      ...

【讨论】:

【参考方案3】:

无我

创建一些对象:

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 作用域的工作原理:

c1.x
>>> 'changed instance'

与自己

更改类不会影响实例:

class foo(object):
    def __init__(self):
        self.x = 'original self'

c1 = foo()
foo.x = 'changed class'
c1.x
>>> 'original self'

【讨论】:

请注意,此示例仅适用于新类型类,与旧类型类的结果相同 @Abdelouahab,是这样吗?我尝试使用旧样式类,并得到与新样式对象相同的结果。 Python 2.7.8【参考方案4】:

我想在我在这个线程和this thread(引用这个)中阅读的回复中添加一些内容。

免责声明:此言论来自我进行的实验

__init__之外的变量

这些实际上是静态类变量,因此,类的所有实例都可以访问。

__init__中的变量

这些实例变量的值只能访问手头的实例(通过self引用)

我的贡献

程序员在使用静态类变量时必须考虑的一件事是它们可能被实例变量所遮蔽(如果您正在访问静态类变量 通过self 引用)。

解释

以前,我认为声明变量的两种方式完全相同(我傻了),部分原因是我可以通过self 引用访问这两种变量。现在,当我遇到麻烦时,我研究了这个话题并把它弄清楚了。

通过静态类变量访问的问题 self 的引用是,如果没有同名的实例变量,它只引用静态类变量,更糟糕的是,试图重新定义一个self 引用的>静态类变量 不起作用,因为创建了一个实例变量,然后它会隐藏以前可访问的静态类变量。 p>

要解决这个问题,您应该始终通过类的名称引用静态类变量

示例

#!/usr/bin/env python

class Foo:
    static_var = 'every instance has access'

    def __init__(self,name):
        self.instance_var = 'I am %s' % 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

我希望这对某人有帮助

【讨论】:

这是这里最重要的答案【参考方案5】:
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.emailUser.firstnameUser.lastname 的值为'none',而对象变量u1.emailu1.firstnameu1.lastname 的值为'abc@xyz'、@ 987654330@ 和'last'

obj.email obj.firstname obj.lastname
('abc@xyz', 'first', 'last')
cls.email cls.firstname cls.lastname
('none', 'none', 'none')

【讨论】:

【参考方案6】:

如果您跟踪类和实例字典,这很容易理解。

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 dict 将只是'two': 50,而类字典将在里面包含'one': 42 键值对。

这就是你应该知道的关于特定变量的全部内容。

【讨论】:

【参考方案7】:

类就像创建对象的蓝图。让我们用盖房子来做个比喻。你有房子的蓝图,所以你可以建造房子。您可以在资源允许的情况下建造尽可能多的房屋。

在这个比喻中,蓝图是类,房子是类的实例化,创建一个对象。

房屋具有共同的属性,例如有屋顶、客厅等。这就是 init 方法的用途。它用你想要的属性构造对象(房子)。

假设你有:

`class house:`
`roof = True`
`def __init__(self, color):`
`self.wallcolor = color`

&gt;&gt; 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'

【讨论】:

【参考方案8】:

在 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

【讨论】:

【参考方案9】:
class foo(object):
    mStatic = 12

    def __init__(self):
        self.x = "OBj"

考虑到 foo 根本无法访问 x (FACT)

现在的冲突在于通过实例或直接通过类访问 mStatic。

从 Python 的内存管理方面考虑:

12 值在内存和名称 mStatic(可从类访问)

指向它。

c1, c2 = foo(), foo() 

这一行创建了两个实例,其中包括指向值 12 的名称 mStatic(直到现在)。

foo.mStatic = 99 

这使得 mStatic 名称指向内存中的一个新位置,其中包含值 99。

并且因为(婴儿) c1 、 c2 仍然跟随(爸爸) foo ,它们具有相同的名称(c1.mStatic 和 c2.mStatic )指向相同的新值。

但是一旦每个婴儿决定独自行走,情况就不同了:

c1.mStatic ="c1 Control"
c2.mStatic ="c2 Control"

从现在到以后,该系列中的每个 (c1,c2,foo) 都有其 mStatica 指向不同的值。

[请尝试在我们谈到的不同状态下对所有 of(c1,c2,foo) 使用 id() 函数,我认为这会让事情变得更好]

这就是我们的真实生活。儿子从父亲那里继承了一些信仰,这些信仰仍然与父亲的信仰相同,直到儿子决定改变它。

希望对你有帮助

【讨论】:

觉得这个答案有用与否!!试试看这个youtube.com/…【参考方案10】:

示例代码:

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()

【讨论】:

但是如果我使用 L 来存储一个 int,我甚至可以更改外部 x 的 L,而无需更改外部 y 的 L...【参考方案11】:

如 S.Lott 所述,

init 之外的变量集属于该类。它们由以下人员共享 所有实例。

init 中创建的变量(以及所有其他方法函数)和 以自我开头。属于对象实例。

但是, 请注意,类变量可以通过 self. 直到它们被具有相似名称的对象变量屏蔽这意味着读取 self. 在为其赋值之前将返回该值Class. 但之后它将返回 obj. 。这是一个例子

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

第二点:考虑slots。它们可能会提供更好的方法来实现对象变量。

【讨论】:

【参考方案12】:

试试这个,看看有什么不同

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')

【讨论】:

以上是关于__init__() 内部和外部变量之间的差异的主要内容,如果未能解决你的问题,请参考以下文章

面向对象编程

python-访问限制

python面向对象编程

python类访问限制

Python面向对象

面向对象进阶