除了名称之外,这些类之间有什么区别吗?
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
,有什么区别吗?
我主要担心的是我会以一种方式使用它,这会给我带来更多的问题。
__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 范围是如何工作的:
c1.x
>>> 'changed instance'
与自我
更改类不会影响实例:
class foo(object):
def __init__(self):
self.x = 'original self'
c1 = foo()
foo.x = 'changed class'
c1.x
>>> 'original self'
我想在我在这个线程和this thread(引用这个)中阅读的回复中添加一些内容。
免责声明:此言论来自我运行的实验
__init__
之外的变量:
实际上,这些是静态类变量,因此可以访问该类的所有实例。
__init__
中的变量:
这些实例变量的值只能访问手头的实例(通过 self
引用)
我的贡献:
程序员在使用静态类变量时必须考虑的一件事是它们可能被实例变量所遮蔽(如果您正在访问静态类变量通过 self
参考)。
解释:
以前,我认为声明变量的两种方式完全相同(我真傻),部分原因是我可以通过 self
引用访问这两种变量。现在,当我遇到麻烦时,我研究了这个话题并把它弄清楚了。
通过 self
引用访问 static 类变量 的问题在于,如果没有 instance 变量,它只会引用 static 类变量相同的名称,更糟糕的是,尝试通过 self
引用重新定义 静态类变量 不起作用,因为创建了一个 实例变量,然后它会隐藏以前可访问的静态类变量。
为了解决这个问题,您应该始终通过类的名称来引用静态类变量。
例子:
#!/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
我希望这对某人有帮助
在 S.Lott 的回复中,类变量被传递给元类新方法,并且可以在定义元类时通过字典访问。因此,甚至可以在创建和实例化类之前访问类变量。
例如:
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'
、{ 9}和'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
示例代码:
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 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
键值对。
这就是您应该了解的有关特定变量的所有信息。
类就像创建对象的蓝图。让我们用盖房子来做个比喻。你有房子的蓝图,所以你可以建造房子。您可以在资源允许的情况下建造尽可能多的房屋。
在这个比喻中,蓝图是类,房子是类的实例化,创建了一个对象。
这些房子有共同的属性,比如有屋顶、客厅等。这就是你 init 方法的用武之地。它使用您想要的属性构造对象(房屋)。
假设您有:
`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'
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() 函数,我认为这会让事情变得更好]
这就是我们的真实生活。儿子从父亲那里继承了一些信仰,这些信仰仍然与父亲的信仰相同,直到儿子决定改变它。
希望它会有所帮助
正如 S.Lott 所指出的,
在 init 之外设置的变量属于该类。它们由所有实例共享。在 init(和所有其他方法函数)中创建并以 self 开头的变量。属于对象实例。
但是,请注意,可以通过 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。它们可能提供更好的方法来实现对象变量。
试试这个并检查差异
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__()
中创建列表/字典/等,它们将在所有实例之间共享。varname
变量集确实属于该类,并且可以通过self.varname
读取,从而为所有实例生成相同的值。但是,当通过实例引用(如self.varname = X
)分配值时,new self.varname 将创建仅为该实例,从而隐藏类变量。类 var 仍然可以通过类引用访问(例如:WithClass.varname
)。并且类变量也可以从任何方法中设置,方法是使用类名 (WithClass.myvar = X
) 而不是实例引用 (self.myvar = X
)。