如何将实例转换为派生类?
我正在尝试在我正在开发的Python程序中使用一点继承。 我有一个基础类,叫做User,它实现了用户的所有功能。 现在我想添加一个未批准用户的概念,这个未批准用户和普通用户很像,只是多了一个方法。
User类里面有一些方法会返回一个User对象。 但是当我创建子类时,这就不行了,因为未批准用户会返回一个User对象,这样我就不能调用这个方法了,还有其他一些问题。
class User(object):
base_dn = 'ou=Users,dc=example,dc=org'
@classmethod
def get(cls, uid):
ldap_data = LdapUtil.get(uid + ',' + self.base_dn)
return User._from_ldap(ldap_data)
class UnapprovedUser(User):
base_dn = 'ou=UnapprovedUsers,dc=example,dc=org'
def approve(self):
new_dn = '' # the new DN
LdapUtil.move(self.dn, new_dn)
get()
和_from_ldap()
这两个方法在两个类中是一样的,不过在未批准用户的get()
方法中需要返回一个未批准用户对象,而不是普通用户。
我该如何把从User.get()
得到的User实例转换成未批准用户呢?
我想做类似这样的事情:
class UnapprovedUser(User):
# continued from before
@classmethod
def get(cls, uid):
user = super(UnapprovedUser, cls).get(uid)
return (UnapprovedUser) user # invalid syntax
这样我就可以包装父类的方法,并简单地把返回的值转换成正确的类。 不过这样做可能会导致父类使用它们的self.base_dn
值,这样会把一切搞坏。
5 个回答
在一个类的方法里,类会通过一个叫做cls的参数传进来。所以你可以用cls.something来代替User.something。就这么简单!
不过,我不太确定你是否应该用两种用户类型。我对你提到的“已批准”不是很明白,这可能有两种意思。
可能是指用户还没有真正登录。在这种情况下,我会为未登录的用户创建一个特殊的匿名用户实例。因为你在批准的时候会移动DN,这样看来你可能就是在做这个。
可能是指用户还没有被批准为正式会员之类的。这其实是权限处理的一个特殊情况,你可能会希望以后有更多的权限。我建议你可以考虑给用户添加角色,把“已批准”当作一个角色。
如果你对“已批准”有其他的意思,可以随意忽略这些建议。:-)
这里的super(UnapprovedUser, self)是错的,应该用super(UnapprovedUser, cls),因为在类方法里你不能用self。
我再重复一下你的问题,在基类里你创建了一个用户,但你想返回一个派生类的对象,比如说:
class User(object):
@classmethod
def get(cls, uid):
return User()
class UnapprovedUser(User):
@classmethod
def get(cls, uid):
user = super(UnapprovedUser, cls).get(uid)
return user # invalid syntax
print UnapprovedUser.get("XXX")
这段代码打印的是用户对象,而不是未批准用户对象。你希望UnapprovedUser.get返回的是UnapprovedUser对象。为此,你可以创建一个工厂函数,它会返回合适的用户,然后再用ldap填充它。
class User(object):
@classmethod
def get(cls, uid):
return cls.getMe()
@classmethod
def getMe(cls):
return cls()
class UnapprovedUser(User):
@classmethod
def get(cls, uid):
user = super(UnapprovedUser, cls).get(uid)
return user
print UnapprovedUser.get("XXX")
这段代码打印的是未批准用户对象。
与其说是“转换”,我觉得你其实是想在调用 UnapprovedUser.get()
时创建一个 UnapprovedUser
,而不是 User
。要做到这一点:
把 User.get
改成真正使用传入的 cls
参数:
@classmethod
def get(cls, uid):
ldap_data = LdapUtil.get(uid + ',' + self.base_dn)
return cls._from_ldap(ldap_data)
在 _from_ldap
中你也需要做类似的事情。你没有列出 _from_ldap
的代码,但我猜它在某个地方做了类似这样的事情:
result = User(... blah ...)
你想把这个替换成:
result = cls(... blah ...)
记住:在 Python 中,类对象是可以调用的,它可以用来创建该类的实例。所以你可以利用类方法中的 cls
参数来创建调用该类方法的类的实例。