Python:使用__getattribute__按名称调用实例成员的方法

时间:2009-11-27 20:22:31

标签: python

我有一个包含成员对象的类。我想将成员的方法称为容器类的方法。

以下示例说明了(但在不同的上下文中)我想要做什么。

假设我们有两个代表两种不同产品的类:玩具鞋子。 每个类都有两个方法,比方说,weight()description(),它们可以显着地返回项目的权重和项目的描述。

现在我想将这些产品放在单独的中 - 每个框只包含一个Toy一个Shoe

我希望仍然能够访问方法weight()description(),但我希望它们是Box实例的成员。另外,我不想在weight()类中专门实现方法description()Box,因为我不想在Box中实现更多方法每当将新方法添加到玩具和鞋子时,就会出现类。

这是一个让我满意的“最终结果”的例子:

# Define our products
product1 = Toy('plastic gun', 200)
product2 = Shoe('black leather shoes', 500)

# Now place them in boxes
box1 = Box(product1, box_code='A1234')
box2 = Box(product2, box_code='V4321')

# I want to know the contents of the boxes
box1.description()
box2.description()

# Desired results
>> plastic gun
>> black leather shoes

我的实施理念是这样的。

class Product():
    def __init__(self, description, weight):
        self.desc = description
        self.wght = weight

    def description(self):
        return self.desc

    def weight(self):
        return self.weight

class Toy(Product):
    pass

class Shoe(Product):
    pass

class Box(object):
    def __init__(self, product, box_code):
        self.box_code = box_code
        self.product = product

    def __getattribute__(self, name):
        # Any method that is accessed in the Box instance should be replaced by
        # the correspondent method in self.product 
        return self.product.__getattribute__(name)

然而这不起作用!如果我运行第一个代码块中显示的“所需”示例,我将获得无限递归。那么,这样做的优雅方式是怎样的呢?

请注意Box有自己的参数box_code,将来我可能想要向ToyShoes添加新方法(例如,促销)代码,或其他),而无需修改类Box

好的,伙计们,我热切期待你的回复。


更新:回答问题 感谢Interjay和Mike Boer的帮助。

我只需要用__getattribute__替换类Box中方法__getattr__的定义,它就像Interjay所建议的那样完美。类Box的更正定义是:

class Box(object):
    def __init__(self, product, box_code):
        self.box_code = box_code
        self.product = product

    def __getattr__(self, name):
        # Any method that is accessed in the Box instance should be replaced by
        # the correspondent method in self.product 
        return self.product.__getattribute__(name)

更新:使用__setattr__

完成示例

在我用getattr方法纠正了问题后,我注意到当我想设置属性时,我的类没有所需的行为。例如,如果我尝试:

box1.desc = 'plastic sword'

而不是更改product1的描述(在box1中封装),在box1中创建了一个名为“desc”的新成员。为了解决这个问题,我必须定义__setattr__函数,如下所示:

def __setattr__(self, name, value):
    setattr(self.product, name, value)

然而,只是添加此函数会创建一个递归调用,因为在__init__我尝试分配调用函数self.product = product的{​​{1}}',它找不到成员{{1}因此,再次无限递地调用函数__setattr__

为了避免这个问题,我必须更改self.product方法,以便使用类'字典分配新成员。然后,类__setattr__的完整定义是:

__init__

现在有趣的事情......如果我首先定义成员Box,如上所示,该程序正常工作。但是,如果我尝试首先定义成员class Box(object): def __init__(self, product, box_code): self.__dict__['product'] = product self.box_code = box_code def __getattr__(self, name): # Any method that is accessed in the Box instance should be replaced by # the correspondent method in self.product return getattr(self.product, name) def __setattr__(self, name, value): setattr(self.product, name, value) ,程序会再次遇到递归循环问题。有人知道为什么吗?

3 个答案:

答案 0 :(得分:3)

您可以按照建议使用__getattr__代替__getattribute__。对于不存在的属性,将调用该属性。

如果您确实想要覆盖(几乎)所有属性访问权限,那么继续使用__getattribute__并进行如此定义:

def __getattribute__(self, name):
    return getattr(object.__getattribute__(self, 'product'), name)

答案 1 :(得分:2)

使用__getattr__代替__getattribute__。如果您使用__getattribute__,则在评估self.product时将获得无限递归。

只有在未找到属性时才会调用

__getattr__,因此不会导致此问题。

答案 2 :(得分:1)

  

现在有趣的事情......如果我首先定义成员self.product,as   如上所示,该程序正常工作。但是,如果我尝试定义   首先是成员self.box_code,程序会受到递归循环的影响   问题了。有人知道为什么吗?

如果你先写self.box_code = box_code,就会发生这种情况:

调用

__setattr__并尝试查找未定义的self.product。 因为它是未定义的__getattr__被调用来解析属性"product",但在__getattr__内还有另一个self.product会导致它成为一个递归循环。