python 在传递参数给 init 时引用类变量
我想实现的目标是:
1. 有一个类变量,用来统计创建了多少个对象
2. 这个变量不应该对对象或其他地方可见,也就是说它应该是类的私有变量
3. 如果在初始化时没有提供特定的ID,就用这个计数变量来给对象的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,所以请帮帮我 :)
3 个回答
你需要写 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()
首先,我把 UserClass
改成 User
(我们知道这是一个类),把 UserID
改成 id
(因为它在 User
类里,所以没必要重复“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__
那样有两个后下划线)是特别的,它用于实现类的私有变量——想了解更多,可以查看 Python 手册中的私有变量。这个特性会触发所谓的“名称改编”。最终,__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_counter
或type(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
等等。
要访问 __user_id_counter
,你需要一个对象或类的引用。在参数列表中,self
或 UserClass
是无法直接访问的,所以:
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