我正在尝试创建一个子类,它充当自定义类的列表。但是,我希望列表继承父类的方法和属性,并返回每个项的数量总和。我试图使用__getattribute__
方法执行此操作,但我无法弄清楚如何将参数传递给可调用属性。下面的高度简化的代码应该更清楚地解释。
class Product:
def __init__(self,price,quantity):
self.price=price
self.quantity=quantity
def get_total_price(self,tax_rate):
return self.price*self.quantity*(1+tax_rate)
class Package(Product,list):
def __init__(self,*args):
list.__init__(self,args)
def __getattribute__(self,*args):
name = args[0]
# the only argument passed is the name...
if name in dir(self[0]):
tot = 0
for product in self:
tot += getattr(product,name)#(need some way to pass the argument)
return sum
else:
list.__getattribute__(self,*args)
p1 = Product(2,4)
p2 = Product(1,6)
print p1.get_total_price(0.1) # returns 8.8
print p2.get_total_price(0.1) # returns 6.6
pkg = Package(p1,p2)
print pkg.get_total_price(0.1) #desired output is 15.4.
实际上,我有很多必须可调用的父类方法。我意识到我可以手动覆盖每个类似列表的子类,但我想避免这种情况,因为将来可能会向父类添加更多方法,我想要一个动态系统。任何建议或意见表示赞赏。谢谢!
答案 0 :(得分:10)
这段代码非常糟糕,根本不是Pythonic。你无法在__getattribute__
中传递额外的参数,所以你不应该尝试像这样做任何隐含的魔法。它会更好地写成:
class Product(object):
def __init__(self, price, quantity):
self.price = price
self.quantity = quantity
def get_total_price(self, tax_rate):
return self.price * self.quantity * (1 + tax_rate)
class Package(object):
def __init__(self, *products):
self.products = products
def get_total_price(self, tax_rate):
return sum(P.get_total_price(tax_rate) for P in self.products)
如果需要,可以使包装器更通用,例如
class Package(object):
def __init__(self, *products):
self.products = products
def sum_with(self, method, *args):
return sum(getattr(P, method)(*args) for P in self.products)
def get_total_price(self, tax_rate):
return self.sum_with('get_total_price', tax_rate)
def another_method(self, foo, bar):
return self.sum_with('another_method', foo, bar)
# or just use sum_with directly
明确比隐含更好。组成通常也比继承更好。
答案 1 :(得分:8)
你有几点困惑:
1)__getattribute__
拦截所有属性访问权限,这不是您想要的。如果真实属性不存在,您只希望代码介入,因此您需要__getattr__
。
2)你的__getattribute__
正在调用列表元素上的方法,但它不应该是真正的工作,它应该只返回一个可调用的东西。请记住,在Python中,x.m(a)
实际上是两个步骤:首先,获取x.m
,然后使用a
参数调用该事物。你的功能应该只是第一步,而不是两个步骤。
3)我很惊讶你需要覆盖的所有方法都要求和。真的有很多方法,真的应该总结一下,是否值得这样做?
此代码可以满足您的需求,但您可能需要考虑更明确的方法,正如其他人所建议的那样:
class Product:
def __init__(self,price,quantity):
self.price = price
self.quantity = quantity
def get_total_price(self,tax_rate):
return self.price*self.quantity*(1+tax_rate)
class Package(list):
def __init__(self,*args):
list.__init__(self,args)
def __getattr__(self,name):
if hasattr(self[0], name):
def fn(*args):
tot = 0
for product in self:
tot += getattr(product,name)(*args)
return tot
return fn
else:
raise AttributeError
此代码中需要注意的事项:我使Package
不是来自Product
,因为它的所有产品都是从委托到列表元素的。请勿使用in dir()
来确定某件事物是否具有属性,请使用hasattr
。
答案 2 :(得分:4)
要回答您的直接问题,您可以调用使用getattr()
检索的函数或方法,方法与调用任何函数的方式相同:将参数(如果有)放在函数引用后的括号中。对函数的引用来自getattr()
而不是属性访问这一事实并没有任何区别。
func = getattr(product, name)
result = func(arg)
可以将这些组合在一起,并消除临时变量func
:
getattr(product, name)(arg)
答案 3 :(得分:2)
除了Cat Plus Plus所说的,如果你真的想要调用魔法(请不要!在实践中有这种方法有令人难以置信的许多令人不安的惊喜等待你),你可以测试它的存在在Product类中创建一个sum_with
动态包装器:
def __getattribute__(self, attr):
return (
lambda *args: self.sum_with(attr, *args)
if hasattr(Product, attr)
else super(Package, self).__getattribute__(attr)
)