Python - 使类装饰器适用于派生类

时间:2011-06-17 09:07:44

标签: python oop decorator

在我们使用Django进行开发的应用程序中,在某些情况下,我们需要为拥有所有者的某些模型自动为用户分配权限(字段名称没有规则,可以是“用户”,“所有者” ,“教练”等,也可以有多个字段。)我的解决方案是创建一个包含那些字段名称的装饰器,它将放在模型定义之前,如下所示(不使用样本中特定于django的代码):

@auto_assign_perms('owner', 'user')
class Test(Base):
    pass

假设Base是在Django的Model类之后派生的抽象类,我在其中添加了在保存对象后分配权限的功能。现在我只打印分配给该类的用户列表。您可以在下面找到装饰器和Base类的代码:

class auto_assign_perms(object):
    def __init__(self, *users):
        self.users = users

    def __call__(self, cls):
        cls.owners.update(self.users)
        return cls


class Base(object):
    owners = set()

    def save(self, *args, **kwargs):
        for owner in self.owners:
            print owner,
        print

我的模型看起来像这样:

@auto_assign_perms('owner', 'user')
class Test(Base):
    pass

@auto_assign_perms('coach')
class Test2(Base):
    pass

问题是两个子类都包含所有三个字段('owner', 'user', 'coach')print self.__class__.__name__方法中的Base.save()正确显示“Test”或“Test2”。我尝试在get_owners()类中添加classmethod Base然后迭代其结果,但它没有帮助。 我怎么解决这个问题?也许我应该使用元类(我还没有得到它们)?提前谢谢。

3 个答案:

答案 0 :(得分:4)

您需要设置所有者列表,而不是更新:

class auto_assign_perms(object):
    def __init__(self, *users):
        self.users = users

    def __call__(self, cls):
        cls.owners = set(self.users) # <- here
        return cls

#some tests
@auto_assign_perms('owner', 'user')
class Test(Base):
    pass

@auto_assign_perms('coach')
class Test2(Base):
    pass


t = Test()
t.save()
t = Test2()
t.save()

>>> 
owner user
coach

答案 1 :(得分:1)

您使用owners作为Base的类变量,因此每当您更改owners时,所有派生类都会看到更改。

要解决此问题,您应该将owners变量定义为派生类的类变量:

class Base(object):

    def save(self, *args, **kwargs):
        for owner in self.owners:
            print owner,
        print

@auto_assign_perms('owner', 'user')
class Test(Base):
     owners = set()

@auto_assign_perms('coach')
class Test2(Base):
     owners = set()

答案 2 :(得分:0)

叫我偏执,但我发现这个解决方案更优雅,因为我认为你根本不需要所有者成为一个类变量:

def auto_assign_perms(*users):

    def class_wrapper(cls):
        class ClassWrapper(cls):
            def __init__(self, owners=users):
                super(cls, self).__init__(owners=owners)

        ClassWrapper.__name__ = cls.__name__
        ClassWrapper.__module__ = cls.__module__

        return ClassWrapper

    return class_wrapper


class Base(object):
    def __init__(self, owners=None):
        if owners is None:
            owners = set()
        self.owners = owners

    def save(self, *args, **kwargs):
        for owner in self.owners:
            print owner,
        print


@auto_assign_perms('owner', 'user')
class Test1(Base):
    pass


@auto_assign_perms('coach')
class Test2(Base):
    pass


class Test3(Base):
    pass


t = Test1(); t.save() # owner user
t = Test2(); t.save() # coach
t = Test3(); t.save() #