假设我们有以下代码:
class A:
var = 0
a = A()
我做明白a.var
和A.var
是不同的变量,我想我明白为什么会发生这种情况。我认为这只是python数据模型的副作用,因为为什么有人想修改实例中的类变量?
然而,今天我遇到了一个类似用法的奇怪例子:它位于谷歌应用引擎db.Model reference中。 Google应用引擎数据存储区假设我们继承了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")
我不明白为什么他们希望我这样做?为什么不引入构造函数并仅使用实例变量?
答案 0 :(得分:3)
db.Model类是经典模型视图控制器设计模式中的“模型”样式类。 其中的每个分配实际上都在数据库中设置列,同时还提供了一个易于使用的界面供您编程。这就是为什么
title="The Three Little Pigs"
将更新对象以及数据库中的列。
有一个构造函数(毫无疑问在db.Model中)处理这个传递逻辑,它将采用关键字args列表并消化它来创建这个关系模型。
这就是变量按照它们的方式设置的原因,以便保持关系。
编辑:让我更好地描述一下。普通类只是为对象设置蓝图。它有实例变量和类变量。由于db.Model的继承性,这实际上是做第三件事:在数据库中设置列定义。为了完成这个第三项任务,它将使EXTENSIVE背后的场景变化,如属性设置和获取。几乎从db.Model继承后,你不再是一个类了,而是一个DB模板。长话短说,这是一个使用类
的非常具体的边缘情况答案 1 :(得分:1)
如果所有变量都声明为instance variables
,那么使用classes
类作为Story
的{{1}}将继承。
答案 2 :(得分:1)
从模型和属性文档中,看起来Model已经覆盖了__getattr__和__setattr__方法,因此,实际上,“Story.title = ...”实际上并没有设置实例属性;相反,它设置存储在实例的属性中的值。
如果你要问故事.__ dict __ ['title'],它会给你带来什么?
答案 3 :(得分:1)
我明白a.var和A.var是不同的变量
首先:截至目前,不,他们不是。
在Python中,您在class
块中声明的所有内容都属于该类。如果实例上没有具有该名称的内容,您可以通过实例查找类的属性。当分配给实例的属性时,实例现在具有该属性,无论之前是否有该属性。 (__init__
,在这方面,只是另一个功能;它由Python的机器自动调用,但它只是将属性添加到一个对象,它没有神奇地指定某种类型该类的所有实例的内容的模板 - 那里有魔术__slots__
类属性,但它仍然没有达到你所期望的那样。)
但是现在,a
没有自己的.var
,因此a.var
指的是A.var
。您可以通过实例修改类属性 - 但请注意修改,而不是替换。当然,这要求属性的原始值是可修改的 - list
符合条件,str
没有。
然而,你的GAE例子完全不同。类Story
具有特别属于"属性"的属性,当你"分配给"时,它可以做各种魔术。他们。这是通过使用课程' __getattr__
,__setattr__
等方法可以更改赋值语法的行为。
答案 4 :(得分:1)
其他答案大部分都是正确的,但却错过了一件至关重要的事情。
如果您定义这样的类:
class Foo(object):
a = 5
和一个实例:
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);
def __init__(self):
self.a = 5
也就是说,您使用构造函数定义一个类,该构造函数将新实例上的a
变量设置为5。
最后,App Engine正在做的是一种完全不同的黑魔法,称为描述符。简而言之,Python允许对象定义特殊的__get__
和__set__
方法。当定义这些特殊方法的类的实例附加到类,并且您创建该类的实例时,尝试访问该属性将不是设置或返回实例或类变量,而是调用特殊的{{ 1}}和__get__
方法。可以找到更全面的描述符介绍here,但这是一个简单的演示:
__set__
现在你可以这样做:
class MultiplyDescriptor(object):
def __init__(self, multiplicand, initial=0):
self.multiplicand = multiplicand
self.value = initial
def __get__(self, obj, objtype):
if obj is None:
return self
return self.multiplicand * self.value
def __set__(self, obj, value):
self.value = value
描述符是令人惊讶的Python语言背后的秘密。例如,class Foo(object):
a = MultiplyDescriptor(2)
bar = Foo()
bar.a = 10
print bar.a # Prints 20!
是使用描述符实现的,方法,静态和类方法以及一堆其他东西也是如此。
答案 5 :(得分:0)
这些类变量是Google App Engine生成模型的元数据。
仅供参考,在您的示例中,a.var == A.var
。
>>> class A:
... var = 0
...
... a = A()
... A.var = 3
... a.var == A.var
1: True