Python中的实例变量与类变量

Python中的实例变量与类变量,python,class,variables,static,member,Python,Class,Variables,Static,Member,我有Python类,其中我在运行时只需要一个实例,因此每个类只有一次属性就足够了,而不是每个实例。如果有多个实例(不会发生),则所有实例都应该具有相同的配置。我想知道下面哪个选项会更好或者更“惯用”Python 类变量: class MyController(Controller): path = "something/" children = [AController, BController] def action(self, request): pass clas

我有Python类,其中我在运行时只需要一个实例,因此每个类只有一次属性就足够了,而不是每个实例。如果有多个实例(不会发生),则所有实例都应该具有相同的配置。我想知道下面哪个选项会更好或者更“惯用”Python

类变量:

class MyController(Controller):

  path = "something/"
  children = [AController, BController]

  def action(self, request):
    pass
class MyController(Controller):

  def __init__(self):
    self.path = "something/"
    self.children = [AController, BController]

  def action(self, request):
    pass
实例变量:

class MyController(Controller):

  path = "something/"
  children = [AController, BController]

  def action(self, request):
    pass
class MyController(Controller):

  def __init__(self):
    self.path = "something/"
    self.children = [AController, BController]

  def action(self, request):
    pass

无论如何,如果您只有一个实例,最好是每个实例都使用所有变量,因为它们的访问速度(稍微)快一点(由于类到实例的“继承”减少了一级“查找”),而且没有任何不利因素可以抵消这一小优势。

如果有疑问,您可能需要一个实例属性

类属性最好保留在有意义的特殊情况下。唯一非常常见的用例是方法。对实例需要知道的只读常量使用类属性并不少见(尽管这样做的唯一好处是,如果您还希望从类外部进行访问的话),但是您当然应该谨慎地在其中存储任何状态,这很少是您想要的。即使只有一个实例,也应该像编写其他实例一样编写该类,这通常意味着使用实例属性。

进一步回应和建议,并添加我自己的颜色

使用实例属性是典型的。。。更地道的Python。由于类属性的用例是特定的,因此它们没有被大量使用。与“普通”方法相比,静态方法和类方法也是如此。它们是处理特定用例的特殊结构,否则它是由一个异常的程序员创建的代码,他们想炫耀自己对Python编程的一些模糊的知识

Alex在回复中提到,由于查找级别降低,访问速度将(稍微)加快。。。让我为那些还不知道这是如何工作的人进一步澄清。它与变量访问非常相似——其搜索顺序为:

  • 当地人
  • 非本地人
  • 全球的
  • 内置
  • 对于属性访问,顺序为:

  • 实例
  • 阶级
  • 由(方法解析顺序)确定的基类
  • 这两种技术都以“由内而外”的方式工作,这意味着首先检查大多数局部对象,然后依次检查外层

    在上面的示例中,假设您正在查找
    path
    属性。当遇到像“
    self.path
    ”这样的引用时,Python将首先查看实例属性以查找匹配项。如果失败,它将检查从中实例化对象的类。最后,它将搜索基类。正如Alex所说,如果在实例中找到了属性,则不需要在其他地方查找,从而节省了一点时间

    但是,如果坚持类属性,则需要额外的查找。或者,您的另一种选择是通过类而不是实例引用对象,例如,
    MyController.path
    而不是
    self.path
    。这是一个直接查找,可以绕过延迟查找,但正如alex在下面提到的,这是一个全局变量,因此您会丢失您认为要保存的部分(除非您创建对[global]类名的本地引用)

    底线是,您应该在大多数情况下使用实例属性。但是,在某些情况下,类属性是作业的正确工具。同时使用这两者的代码需要非常谨慎,因为使用self只会让您获得实例属性对象和对相同名称的class属性的阴影访问。在这种情况下,您必须使用通过类名访问属性才能引用它。

    相同的问题在-这里的代码改编自@Edward Loper

    局部变量是访问速度最快的,与模块变量紧密相连,其次是类变量,最后是实例变量

    您可以从以下4个范围访问变量:

  • 实例变量(self.varname)
  • 类变量(Classname.varname)
  • 模块变量(VARNAME)
  • 局部变量(varname)
  • 测试:

    import timeit
    
    setup='''
    XGLOBAL= 5
    class A:
        xclass = 5
        def __init__(self):
            self.xinstance = 5
        def f1(self):
            xlocal = 5
            x = self.xinstance
        def f2(self):
            xlocal = 5
            x = A.xclass
        def f3(self):
            xlocal = 5
            x = XGLOBAL
        def f4(self):
            xlocal = 5
            x = xlocal
    a = A()
    '''
    print('access via instance variable: %.3f' % timeit.timeit('a.f1()', setup=setup, number=300000000) )
    print('access via class variable: %.3f' % timeit.timeit('a.f2()', setup=setup, number=300000000) )
    print('access via module variable: %.3f' % timeit.timeit('a.f3()', setup=setup, number=300000000) )
    print('access via local variable: %.3f' % timeit.timeit('a.f4()', setup=setup, number=300000000) )
    
    结果是:

    access via instance variable: 93.456
    access via class variable: 82.169
    access via module variable: 72.634
    access via local variable: 72.199
    

    类变量是一种只读常量。如果Python允许我定义常量,我会将其写成常量。@deamon,我更可能将常量完全放在类定义之外,并将它们命名为所有大写。把它们放在教室里也可以。让它们成为实例属性不会有什么坏处,但可能有点奇怪。我不认为这是一个社区过分支持其中一个选项的问题。@MikeGraham FWIW建议避免使用全局变量而使用类变量。但也有例外。这里有一个指向的新链接。现在这里只写了:
    避免全局变量
    ,它们的定义是,全局变量也是声明为类属性的变量。然而,Python自己的样式指南()应该是此类问题的首选。那么你自己的思维应该是你选择的工具(当然你也可以从谷歌那里得到想法),你从来没有听说过博格模式吗?一开始只有一个实例是错误的。@Devin,是的,我听说过博格模式,因为我是介绍它的人(2001年,cfr;-)。但是,在简单的情况下,仅仅拥有一个实例而不强制执行并没有什么错。@user1767754,用
    python-mtimeit
    自己制作它们很容易——但在python3.4中刚刚这么做之后,我注意到访问
    int
    类变量实际上比我的旧工作站上的实例变量快5到11纳秒——不确定是什么代码路径造成的。@wescpy,但是查看了
    MyController