如何将实例转换为派生类?

17 投票
5 回答
64377 浏览
提问于 2025-04-15 12:52

我正在尝试在我正在开发的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 个回答

1

在一个类的方法里,类会通过一个叫做cls的参数传进来。所以你可以用cls.something来代替User.something。就这么简单!

不过,我不太确定你是否应该用两种用户类型。我对你提到的“已批准”不是很明白,这可能有两种意思。

  1. 可能是指用户还没有真正登录。在这种情况下,我会为未登录的用户创建一个特殊的匿名用户实例。因为你在批准的时候会移动DN,这样看来你可能就是在做这个。

  2. 可能是指用户还没有被批准为正式会员之类的。这其实是权限处理的一个特殊情况,你可能会希望以后有更多的权限。我建议你可以考虑给用户添加角色,把“已批准”当作一个角色。

如果你对“已批准”有其他的意思,可以随意忽略这些建议。:-)

1
  • 这里的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")

这段代码打印的是未批准用户对象。

11

与其说是“转换”,我觉得你其实是想在调用 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 参数来创建调用该类方法的类的实例。

撰写回答