Python 在类定义中实例化类

Posted

技术标签:

【中文标题】Python 在类定义中实例化类【英文标题】:Python Instantiate Class Within Class Definition 【发布时间】:2013-01-28 06:29:03 【问题描述】:

我正在尝试将一个变量添加到一个包含该类实例的类中。以下是我的代码的缩短版本。

class Classy :
    def __init__(self) :
        self.hi = "HI!"
    # "CLASSIES" variable holds instances of class "Classy"
    CLASSIES = []
    for i in xrange(0,4) :
        CLASSIES.append(Classy())

运行代码时,出现以下错误。

Traceback (most recent call last):
  File "classy.py", line 6, in Classy
    CLASSIES.append(Classy())
NameError: name 'Classy' is not defined

是否有另一种方法可以将类的实例添加到该类中的类/静态变量?

【问题讨论】:

真正的问题是你想用这个动作来完成什么 @joojaa 我不会说这个用例是晦涩的。 嗯,有点,我会理解如果你真的初始化了孩子,但把它们放在类变量中会使这变得晦涩难懂。您基本上启动了具有 4 个固定其他自我的不同实例的东西。但是可能有许多不同的 goto 人。奇怪的是,经典实例不是列表的一部分,如果启动的类是列表的一部分,我会理解这一点。或者你在看一系列博格? 【参考方案1】:

类体在类创建之前执行。因此,您正在尝试在该类存在之前对其进行实例化。您仍然可以将实例附加到类,但您必须在类主体完成后创建它们,例如:

class Classy(object):
    def __init__(self):
        self.hi = "HI!"
    CLASSIES = []

for i in xrange(4):
    Classy.CLASSIES.append(Classy())

但是,我建议您首先仔细考虑一下您是否真的需要这个有效的全局列表,以及您是否需要它成为类对象的一部分。就个人而言,我几乎从不做这样的事情。

【讨论】:

这是对我的回答的欺骗。实际上,你也犯了同样的错误——也应该是Classy.CLASSIES.append() @Lattyware 如果它在代码旁边包含更多解释,这真的是一个骗局吗?此外,代码也有点不同。不过感谢指出错误! 您的解释与 BrenBarn 在他的回答中所说的重复,最后您的建议对我来说似乎是错误的 - 它不是实际上是全球性的 - 它是班级级别的,而很少见,有这样的用例并不是那么遥不可及。代码差异也是微不足道的。这不是一个糟糕的答案,只是我真的没有看到它添加了任何未提及的内容。 @Lattyware 虽然很遗憾我们现在有三个内容大部分重叠的答案,但我不认为其中一个会稍后出现意味着我们必须删除任何一个。 RE技术问题:列表实际上是全局的——类是全局的,并且类属性是每个类一次,因此(1)可以从任何地方访问,(2)每个访问的人都可以共享它。如果不是太麻烦的话,我想听一个用例——我写的是实话,我几乎从没见过这样的代码。 是的,很高兴知道这段代码的用例。因为这似乎是一种很好的射击自我的方式。顺便提一句。即使你打字慢一点,它仍然是一个骗子,这与我的讨论无关。【参考方案2】:

在我看来,您想要获得:

class Classy :
    CLASSIES = []
    def __init__(self) :
        self.hi = "HI!"
        Classy.CLASSIES.append(self)

for i in xrange(4):
    Classy()

for x in  Classy.CLASSIES:
    print x

结果

<__main__.Classy instance at 0x011DF3F0>
<__main__.Classy instance at 0x011DF440>
<__main__.Classy instance at 0x011DF418>
<__main__.Classy instance at 0x011DF2B0>

编辑

注意使用Lattyware的代码:

class Classy :
    CLASSIES = []
    idC = id(CLASSIES)
    def __init__(self) :
        self.hi = "HI!"
        #Classy.CLASSIES.append(self)


Classy.CLASSIES = [Classy() for _ in xrange(0,4)]

print Classy.idC
print id(Classy.CLASSIES)
print 'Classy.idC==id(Classy.CLASSIES) :',Classy.idC==id(Classy.CLASSIES)

结果

18713576
10755928
Classy.idC==id(Classy.CLASSIES) : False

虽然使用delnan'code的for循环,但不会出现。

但是很容易纠正: 写作Classy.CLASSIES[:] = [Classy() for _ in xrange(0,4)]Classy.CLASSIES.extend(Classy() for _ in xrange(0,4)) 而不是Classy.CLASSIES = [Classy() for _ in xrange(0,4)] 这取决于想要什么。

编辑 2

方法可以像普通方法一样引用全局名称 职能。与方法关联的全局范围是模块 包含它的定义。 (类永远不会用作全局范围。)

http://docs.python.org/2/tutorial/classes.html#class-definition-syntax

一个类有一个由字典对象实现的命名空间。班级 属性引用被翻译成这本词典中的查找, 例如,C.x 被翻译成C.__dict__["x"]

http://docs.python.org/2/reference/datamodel.html#new-style-and-classic-classes

class Classy :
    CLASSIES = []

print '"CLASSIES" in globals()',"CLASSIES" in globals()
print '"CLASSIES" in Classy.__dict__ ==',"CLASSIES" in Classy.__dict__

结果

"CLASSIES" in globals() False
"CLASSIES" in Classy.__dict__ == True

Delnan,你将如何继续假装 CLASSIES 是全球性的?? 在您与 Lattyware 的辩论中,我是否误解了某些内容?

【讨论】:

这假定 OP 希望将 每个 类添加到列表中。 @Lattyware 是的,它假定,但我写了“在我看来”,意思是我不确定。而且我仍然不确定。令人讨厌的是它通常很难理解这些问题是因为提问者没有提供足够的信息来说明他们真正想要什么以及问题的背景。 这并不是一个真正的缺陷——你为什么要再次引用该列表,而不是通过Classy.CLASSIES 访问它——这是可能的,但你必须在完成定义之前这样做类,这意味着你永远不需要这样做。 @Lattyware 我不明白你的最后评论。你说什么? 列表推导是一个新列表这一事实无关紧要 - 因为没有创建对旧列表的引用的用例。【参考方案3】:

执行此操作的最简单方法是在创建类之后执行此操作,此时已定义类,因此可以使用:

class Classy :
    CLASSIES = []

    def __init__(self) :
        self.hi = "HI!"

Classy.CLASSIES = [Classy() for _ in xrange(0,4)]

(为了方便起见,这里使用list comprehension,因为它是构建列表的最易读和最有效的方式)。

还请注意,如果它打算成为一个常量,您可能应该将其设为元组而不是列表,如果不打算这样做,您可能不应该使用 ALL_CAPS 名称,按照惯例, 表示一个常数。

【讨论】:

是的,list comp. 构建一个列表并创建一个新列表,这与执行定义的结果不同类,虽然相同的列表可能很重要,但可能只是 updtaed 。在我的回答中查看我的编辑。 @eyquem 这是最初的创建,所以没关系。模拟一下,只需删除类中的初始创建 - 我只是觉得值得放在那里,因为它不太可能很重要,并且它暗示该列表将存在,(包括编辑)。如果您出于某种原因需要它存在并且身份保持不变,只需使用+= 而不是= 好吧,如果因为它是初始创建而无关紧要,那么在类的定义中不需要CLASSIES = [] 行。 BrenBarn 的代码就是这种情况 @eyquem 是的,没有必要,但是,这是个好主意。它让代码的读者知道类属性的存在,它让任何具有自动完成功能的编辑器都知道它很容易存在。这使得它值得,即使它在功能上不需要。它使代码更具可读性。 顺便问一下,delnan 说 CLASSIES 是全球级别的,你有没有被说服?我是不是明白了什么?在我看来,这是毫无意义且不可持续的。他从哪里得出这个奇怪的想法?【参考方案4】:

类本身直到类块执行完毕后才被定义,所以你不能在它自己的定义中使用类。

您可以在创建类后使用类装饰器或元类来添加所需的类变量。这是一个带有装饰器的示例。

def addClassy(cls):
    cls.CLASSIES = [cls() for a in xrange(4)]
    return cls

@addClassy
class Classy(object):
    pass

>>> Classy.CLASSIES
0: [<__main__.Classy object at 0x000000000289A240>,
 <__main__.Classy object at 0x000000000289A518>,
 <__main__.Classy object at 0x000000000289A198>,
 <__main__.Classy object at 0x000000000289A208>]

【讨论】:

虽然这行得通,但感觉有点……不对劲——对我来说,很难看出这个类有 CLASSIES 属性,以及其中会有什么。 @Lattyware:这取决于您需要多久执行一次。如果您需要为多个类使用这种模式,那么创建一个装饰器来完成它是有意义的,而不是重复代码来为每个类手动完成。 在这种情况下,它会更有意义,是的。 @BrenBarn 我没有通过执行代码来验证,但在我看来 addClassy( ) 将属性 CLASSIES 添加到类,即使其中已经有一个(您的代码不是这种情况)。在某些情况下,列表不会改变可能很重要。请参阅我的答案中的编辑,您的代码在这一点上类似于 Lattyware 的代码。

以上是关于Python 在类定义中实例化类的主要内容,如果未能解决你的问题,请参考以下文章

python入门class类

Python 基础入门 6_1 类与对象

python 基础

在 Python 中实例化类的区别

实例化类的 Python3 问题

在 Python 中导入后自动实例化类