【问题标题】:python refer to class variable while passing arguments to initpython在将参数传递给init时引用类变量
【发布时间】:2011-10-14 01:13:59
【问题描述】:

我想要达到的目标:
1. 有一个类变量来记录创建的对象数量
2.该变量不应该对对象/其他人可用,即类私有
3.如果初始化时没有提供具体的ID,使用这个计数器变量来分配object.ID
我有以下python代码

class UserClass(object) :
    __user_id_counter = 0
    def __init__(self, UserID=__user_id_counter) :
        self.UserID = UserID
        __user_id_counter += 1

myuser = UserClass()

但我得到了 UnboundLocalError: local variable '_UserClass__user_id_counter' referenced before assignment
我是 python 新手,所以请在这里帮助我 :)

【问题讨论】:

  • 不要编辑问题以表明答案。通过单击勾号将您想要的答案标记为答案。
  • @chris-morgan :谢谢,我会记住这一点的。

标签: python arguments default init class-variables


【解决方案1】:

要访问__user_id_counter,需要一个对象或类引用。在参数列表中selfUserClass无法访问,因此:

class UserClass(object) :
    __user_id_counter = 0
    def __init__(self, UserID=None) :
        self.UserID = self.__user_id_counter if UserID is None else UserID
        UserClass.__user_id_counter += 1

【讨论】:

  • 谢谢,这解决了参数传递的问题,但是当创建下一个对象时它不会增加计数器。使用您的代码,我创建了 3 个对象,但它们都有 0 作为 UserID
  • 你必须增加类变量。您可以使用self.__class__.__user_id_counter 执行此操作,就像我的回答一样。
  • 所以我应该做 self.__user_id_counter += 1 而不是 self.__class__.__user_id_counter += 1 ??
  • 答案是肯定的。非常感谢 Maxim-Yegorushkin 和 @Constantinius。
  • 投反对票,因为这个答案解决了由于在self而不是UserClass中更改它而导致的问题。
【解决方案2】:

你必须写self.__user_id_counter。如果没有找到实例变量,这也会查找类变量。

编辑: Sudhi 指出,在参数列表中,self 声明无效。要避免此问题,请尝试以下操作:

class UserClass(object) :
    __user_id_counter = 0
    def __init__(self, UserID=None) :
        if UserID is None:
            self.UserID = self.__user_id_counter
            self.__class__.__user_id_counter += 1
        else:
            self.UserID = UserID

myuser = UserClass()

【讨论】:

  • 如果我这样做 __init__(self, UserID=self.__user_id_counter) : 它会说 NameError name self not definied
  • 我明白了。似乎这在参数列表中是不可能的。我想你必须做一个小把戏。我会更新我的答案。
【解决方案3】:

首先,我将 UserClass 重命名为 User(我们知道它是一个类)和 UserID 重命名为 id(它在 User 类中,因此无需重复“用户”位,Python 风格推荐小写),__user_id_counter__id_counter(同上)。阅读 PEP 8 了解 Python 风格指南。

那么,我们的出发点是:

class User(object):
    __id_counter = 0
    def __init__(self, id=None) :
        self.id = self.__id_counter if id is None else id
        __id_counter += 1
myuser = User()
myuser2 = User()
myuser3 = User()

现在,双前导下划线(没有双尾下划线,如 __init____metaclass__)是特殊的,用于实现类私有变量 - 阅读 private variables in the Python manual 了解更多信息.它触发了所谓的“名称修改”。最终结果是__id_counter 在整个班级中被重命名为_User__id_counter。这既有用又危险。在这种情况下,我认为可能只是没有必要。

这可能会导致子类出现问题。

class SpecialUser(User):
    def __init__(self):
        self.__id_counter

现在,SpecialUser() 将触发 AttributeError: 'SpecialUser' object has no attribute '_SpecialUser__id_counter'

那么问题是,您希望子类具有相同的 ID 计数器,还是它们自己的 ID 计数器?

  • 如果您希望他们拥有相同的 ID 计数器,请使用User.__id_counter不要self.__class__.__id_countertype(self).__id_counter+= 1 一起使用,因为对于SpecialUser 实例,将SpecialUser._User__id_counter 设置为User._User__id_counter + 1,然后将SpecialUser 设置为从现在开始使用它自己的 _User__id_counter,这与你想要的相差甚远。

  • 如果您希望他们拥有他们自己的 ID 计数器,请从使用 _id_counter 而不是 __id_counter 开始,因为名称重整会将您带到您不想去的方向.

    您可以采取以下几种方法:

    • 使用元类为每个类赋予其自己的计数器变量(这是一个比我目前懒得写的更高级的话题,如果您愿意,请询问更多细节)。

    • 在每个类定义中加入一行_id_counter = 0。 (如果你不这样做,你会在 ID 起点上遇到麻烦 - 制作很多它们,例如 User(), User(), SpecialUser(), User(), SpecialUser(), SpecialUser(), User(),它们的 ID 将分别为 0, 1, 2, 2, 3, 4, 3 而不是 0, 1, 0, 2, 1, 2

    此处使用名称修饰方法会导致另一个低效率的问题是子类中将有多个计数器 - _User__id_counter_SpecialUser__id_counter 等。

【讨论】:

  • 这太好了,现在我明白了当我声明 __id_counter 时 python 内部发生了什么,我想我会坚持使用 same ID 计数器的前一种情况并替换我的__id_counter_id_counter。非常感谢克里斯,我相信我会用更多这样的 python 查询来困扰你:D 太糟糕了,我不能为我的问题打多个答案:(
  • @Sudhi:如果你愿意,你可以投票(点击向上箭头)。
  • 不能,没有足够的声誉:(当我在 stackoverflow 上获得更多声誉时我会这样做
猜你喜欢
  • 1970-01-01
  • 2017-07-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-05
相关资源
最近更新 更多