Python中的动态复合委托

时间:2017-06-11 09:05:00

标签: python delegates

更新:我找到了一个问题的答案:
如果没有从__getattr__生成递归调用,我怎么能做我认为的'动态复合委托'?

此时我对功能感到满意。我只希望我能找到一个不需要__getattr__装饰器的解决方案。装饰器对于调试是有问题的,并且创建只能应用于一个方法(__geattr__)的装饰器似乎不是Pythonic。

通过向您的班级添加__getattr__和装饰器来使用动态委派:

class Story():
    @delegate(delegate_to='chapter book', 
              only_delegate='title')
    def __getattr__(self, attr_name):
        raise AttributeError("'%s' object has no attribute '%s'" %
                             (self.__class__.__name__,attr_name))

它的工作原理如下:

class Other():
    """ Placeholder for Chapter and Book classes """

my_story = Story()
chp1 = Other()
my_story.chapter = chp1
bk = Other()  
my_story.book = bk

#delegation, with 'chapter' having precedence over 'book'
bk.title = "Title from book"
chp1.title = "Title from chapter"
assert my_story.title == "Title from chapter"
del chp1.title
assert my_story.title == "Title from book"

#local override
my_story.title = "Title from story"
assert my_story.title == "Title from story"

delegate()的代码是:

def delegate(delegate_to:str, only_delegate:str=''):

    #split into lists at decoration time so we don't have split each time
    #an attribute is delegated
    delegate_to = delegate_to.split()
    only_delegate = only_delegate.split()

    def ga_decorator(fn):
        #the decorator can only be applied to getattr, not Pythonic :-(
        if fn.__name__ != '__getattr__' and fn.__name__ != 'ga_decorated':
            raise AttributeError("Delegation can only apply to '__getattr__'")

        def ga_decorated(self, attr_name):

            #To enable delegation on any attribute, set only_delegate=''
            if not only_delegate or (attr_name in only_delegate):

                #delegate_to is a list of members of 'self' that reference 
                #the objects we will delegate to. 
                for m in delegate_to:
                    if m in dir(self):
                        member = getattr(self, m)
                        if attr_name in dir(member):
                            return getattr(member, attr_name)

            return fn(self, attr_name)
        return ga_decorated 
    return ga_decorator       

only_delegate列表可以包含方法和委托以相同的方式工作。

如果要将一个属性委托给一组对象,将其他属性委托给另一组对象,请在更多委托语句上进行堆栈:

class Story():
    @delegate(delegate_to='collection', 
              only_delegate='archivist')
    @delegate(delegate_to='chapter book', 
              only_delegate='title author')
    def __getattr__(self, attr_name):
        raise AttributeError("'%s' object has no attribute '%s'" %
                             (self.__class__.__name__,attr_name))

1 个答案:

答案 0 :(得分:0)

避免在__getattr__中创建递归__getattr__来电的一种方法是替换

hasattr(obj, attr_name)

attr_name in dir(obj)

在装饰者中,这个:

            for m in delegate_to.split():

                #why does this cause recursion?
                if hasattr(self, m):
                    member = getattr(self, m)
                    if hasattr(member, attr_name):
                        return getattr(member, attr_name)

变为

            for m in delegate_to.split():
                if m in dir(self):
                    member = getattr(self, m)
                    if attr_name in dir(member):
                        return getattr(member, attr_name)

根据我的测试结果,dir()似乎无法间接拨打getattr