Python类变量的使用

Python类变量的使用,python,google-app-engine,python-2.7,class-variables,Python,Google App Engine,Python 2.7,Class Variables,假设我们有以下代码: class A: var = 0 a = A() 我知道a.var和a.var是不同的变量,我想我知道为什么会发生这种情况。我认为这只是python数据模型的一个副作用,因为为什么有人要修改实例中的类变量 然而,今天我遇到了一个奇怪的例子:它在谷歌应用程序引擎中。Google app engine数据存储假定我们继承db.Model类,并引入键作为类变量: class Story(db.Model): title = db.StringProperty()

假设我们有以下代码:

class A:
    var = 0
a = A()
我知道
a.var
a.var
是不同的变量,我想我知道为什么会发生这种情况。我认为这只是python数据模型的一个副作用,因为为什么有人要修改实例中的类变量

然而,今天我遇到了一个奇怪的例子:它在谷歌应用程序引擎中。Google app engine数据存储假定我们继承
db.Model
类,并引入键作为类变量:

class Story(db.Model):
    title = db.StringProperty()
    body = db.TextProperty()
    created = db.DateTimeProperty(auto_now_add=True)
s = Story(title="The Three Little Pigs")

我不明白他们为什么要我这样做?为什么不引入一个构造函数并只使用实例变量呢?

如果所有变量都声明为
实例变量
,那么
使用
故事
类作为
超类
不从中继承任何内容。

这些类变量是Google App Engine生成它们的元数据模型

仅供参考,在您的示例中,
a.var==a.var

>>> class A:
...     var = 0
... 
... a = A()
... A.var = 3
... a.var == A.var
1: True

Model类是经典模型-视图-控制器设计模式中的“模型”样式的类。 其中的每个作业实际上都是在数据库中设置列,同时也为您提供了一个易于使用的编程界面。这就是为什么

title="The Three Little Pigs"
将更新对象以及数据库中的列

有一个构造函数(毫无疑问在db.Model中)处理这个传递逻辑,它将获取关键字args列表并对其进行摘要以创建这个关系模型

这就是为什么变量是按原样设置的,以便保持关系


编辑:让我更好地描述一下。普通类只是为对象设置蓝图。它有实例变量和类变量。由于对db.Model的继承,这实际上是在做第三件事:在数据库中设置列定义。为了完成第三项任务,它需要在场景后面对属性设置和获取等内容进行大量更改。几乎一旦您从db.Model继承,您就不再是一个真正的类,而是一个db模板。长话短说,这是使用模型和属性文档中类的一个非常特殊的边缘情况,看起来模型已经覆盖了uu getattr_uu和u setattr_u方法,因此实际上,“story.title=…”并没有实际设置实例属性;相反,它设置与实例属性一起存储的值

如果你问故事,它会给你什么

我知道a.var和a.var是不同的变量

首先:到目前为止,不,他们不是

在Python中,在
块中声明的所有内容都属于该类。如果实例还没有具有该名称的内容,则可以通过实例查找类的属性。将实例的属性指定给时,该实例现在具有该属性,而不管它以前是否具有该属性。(
\uuuu init\uuuu
,在这方面,它只是另一个函数;它由Python的机器自动调用,但它只是向对象添加属性,它不会神奇地为类的所有实例的内容指定某种类型的模板-有神奇的
\uuuu slots\uuu
类属性,但它仍然不这样做这正是你所期望的。)

但是现在,
a
没有自己的
.var
,所以
a.var
指的是
a.var
。您可以通过实例修改类属性,但请注意,修改,而不是替换。当然,这要求属性的原始值是可修改的-符合
列表
条件的,不符合
str
条件的


不过,你的GAE例子完全不同。类
Story
具有特定于“属性”的属性,当您“指定”它们时,这些属性可以发挥各种魔力。这是通过使用类“
\uuu getattr\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu”和
等方法来更改赋值语法的行为来实现的。

其他答案基本上是正确的

如果定义这样的类:

class Foo(object):
  a = 5
class Foo(object);
  def __init__(self):
    self.a = 5
class Foo(object):
  a = MultiplyDescriptor(2)

bar = Foo()
bar.a = 10
print bar.a # Prints 20!
还有一个例子:

myinstance = Foo()
然后
Foo.a
myinstance.a
是非常相同的变量。更改一个将更改另一个,如果创建多个
Foo
实例,则每个实例上的
.a
属性将是相同的变量。这是因为Python解析属性访问的方式:首先在对象的dict中查找,如果在那里找不到,则在类的dict中查找,以此类推

鉴于变量的共享性质,这也有助于解释为什么赋值不能按预期的方式工作:

>>> bar = Foo()
>>> baz = Foo()
>>> Foo.a = 6
>>> bar.a = 7
>>> bar.a
7
>>> baz.a
6
这里发生的事情是,当我们分配给
Foo.a
时,它修改了
Foo
的所有实例在请求
instance.a
时通常解析的变量。但是当我们分配给
bar.a
时,Python在该实例上创建了一个名为
a
的新变量,它现在屏蔽了类变量-从现在起,该特定实例将始终看到它自己的局部值

如果希望类的每个实例都有一个单独的变量初始化为5,通常的方法如下:

class Foo(object):
  a = 5
class Foo(object);
  def __init__(self):
    self.a = 5
class Foo(object):
  a = MultiplyDescriptor(2)

bar = Foo()
bar.a = 10
print bar.a # Prints 20!
也就是说,您使用构造函数定义一个类,该构造函数将新实例上的
a
变量设置为5

最后,AppEngine所做的是一种完全不同的叫做描述符的黑魔法。简而言之,Python允许对象定义特殊的
\uuuuuuu get\uuuuu
\uuuu set\uuuu
方法。当定义这些特殊方法的类的实例附加到某个类,并且您创建了该类的实例时,尝试访问该属性的行为将不会设置或返回实例或类变量,而是