请帮助我理解Python类与实例变量的区别

3
我是一位Python的新手,对于Python如何解释类和实例变量还有些困惑。我的背景是C#,因此我对面向对象编程(从C#角度)有相当不错的理解,然而我在Python上还是有些疑惑。我猜测这是因为我用了错误的思维方式。
以下是一个类的示例:
class User():
    """The default user implementation"""

    # define the variables
    id = None
    first_name = None
    last_name = None
    email = None
    password = None
    created = None
    deleted = False

    def __init__(self):
        """Creates a new instance of the User object."""
        self.is_admin = False

从我读到的文档来看,所有的id、first_name、last_name等都是类属性,这些属性在实例之间共享,换言之,在C#中这些属性将为“静态”属性。而is_admin则是实例属性,仅限于对象的特定实例。换言之,在C#中,这些将是字段或属性。
然而,当我做如下操作时,就有点疑惑:
new_user = User() new_user.id = "blah" new_user.last_name = "lasty"
new_user1 = User() new_user1.id = "some_id" new_user1.last_name = "firsty"
这会将值设为:
new_user.id = "blah" new_user.last_name = "lasty"
new_user1.id = "some_id" new_user1.last_name = "firsty"
鉴于id和last_name被定义为类属性,我原本认为对new_user1对象的调用会覆盖"blah"和"lasty"的值,但是每个实例都保留了分���给它的值。因此,我有点疑惑。
如果这是正常的,请问有人能说明一下原因吗?那么在这种情况下,如何定义静态变量呢?
谢谢, Justin

1
Toptal今天发布了一篇非常相关的文章,非常好地解释了这个问题。http://bit.ly/1cw8MiQ - Slater Victoroff
4个回答

3

Python会在实例上查找属性,然后在实例的类、任何基类和它们的父级上查找。因此,idlast_name是实例属性,它们隐藏了类上同名的属性。如果您访问一个实例上未设置的属性(例如first_name),则会获取类的属性,因为该属性未在实例上找到。


1

最简单的方法是展示类变量和实例变量之间的差异:

>>> import datetime
>>> class Example:
...     my_name = 'example'
...     def __init__(self):
...         self.now = datetime.datetime.now()
...
>>> e = Example()
>>> e.my_name
'example'
>>> e.now
datetime.datetime(2015, 6, 1, 2, 2, 58, 211399)
>>> d = Example()
>>> d.my_name
'example'
>>> d.now
datetime.datetime(2015, 6, 1, 2, 4, 21, 165731)
>>>

my_name是一个类变量——它是类的属性,任何类的实例都可以访问该属性。now是一个实例变量——只有Example类型的对象可以访问datetime,每个对象的datetime都不同。

为了强调这一点,让我们尝试从类本身访问每个变量:

>>> Example.now
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'Example' has no attribute 'now'
>>> Example.my_name
'example'
>>>

一个对象可以修改该实例的类变量:
>>> d.my_name = "George"
>>> d.may_name
'George'
>>> Example.my_name
'example'

但不适用于类。

Static是C和C++中引用静态链接的关键字,并且在许多现代OO语言中用于表示由类的所有对象共享的代码片段,但并不是类方法或变量本身。Python使用staticmethod修饰符来修饰方法,但没有静态变量,只有类变量。

我希望这样能澄清事情。


0

Python没有常量或静态变量。对于类属性也是一样的。类在导入时加载该值。如果用户更改了导入的值。

class MyClass(object):
    id = 1

def main():

    myclass = MyClass()
    print("class1", myclass.id)
    print()

    myclass2 = MyClass()
    myclass2.id = 2
    print("class1", myclass.id)
    print("class2", myclass2.id)
    print()

    MyClass.id = 3
    myclass3 = MyClass()
    print("class1", myclass.id)
    print("class2", myclass2.id)
    print("class3", myclass3.id)
    print()

    myclass4 = MyClass()
    print("class1", myclass.id)
    print("class2", myclass2.id)
    print("class3", myclass3.id)
    print("class4", myclass4.id)
    print()

    myclass5 = MyClass()
    myclass5.id = 5 
    print("class1", myclass.id)
    print("class2", myclass2.id)
    print("class3", myclass3.id)
    print("class4", myclass4.id)
    print("class5", myclass5.id)
# end main

if __name__ == '__main__':
    main()

结果很有趣。

class1 1

class1 1
class2 2

class1 3
class2 2
class3 3

class1 3
class2 2
class3 3
class4 3

class1 3
class2 2
class3 3
class4 3
class5 5

如果您没有设置实例 ID,它将默认为类 ID。

MyClass.id = 1

如果实例没有设置值,这将更改实例的值。


0

类变量在C和Java中使用的术语意义上并不真正是“静态”的。它们很容易被覆盖。最好将它们视为与类对象关联的单个副本默认值。

考虑(在Python 3中):

class Foo(object):
    cv='cv'
    def __setattr__(self, name, value):
        if name in self.__class__.__dict__:
            print('Overloading class var "{}" with instance attribute "{}"'.format(self.__class__.__dict__[name], value))
            super(self.__class__, self).__setattr__(name, value)

    def __getattribute__(*args):
        self, name, *the_rest=args
        if name in Foo.__dict__:
            print('there is a class variable called "{}" with value of: "{}"'.format(name, Foo.__dict__[name]))      
        return object.__getattribute__(*args)  

现在实例化类Foo的一个副本并测试类变量和实例变量:
>>> foo=Foo()
>>> foo.cv
there is a class variable called "cv" with value of: "cv"
'cv'
>>> foo.cv='IV'
Overloading class var "cv" with instance attribute "IV"
>>> foo.cv
there is a class variable called "cv" with value of: "cv"
'IV'
>>> Foo.cv='NEW CV'
>>> bar=Foo()
>>> bar.cv
there is a class variable called "cv" with value of: "NEW CV"
'NEW CV'

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接