使用 __getattribute__ 或 __getattr__ 调用 Python 中的方法

发布于 2024-12-02 10:05:57 字数 1132 浏览 3 评论 0原文

我正在尝试创建一个充当自定义类列表的子类。但是,我希望列表继承父类的方法和属性,并返回每个项目的数量总和。我尝试使用 __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.

实际上,我有许多父类的方法,它们必须是可调用的。我意识到我可以手动重写类似列表的子类的每个方法,但我想避免这种情况,因为将来可能会向父类添加更多方法,并且我想要一个动态系统。如有任何意见或建议,我们将不胜感激。谢谢!

I am trying to create a subclass which acts as a list of custom classes. However, I want the list to inherit the methods and attributes of the parent class and return a sum of the quantities of each item. I am attempting to do this using the __getattribute__ method, but I cannot figure out how to pass arguments to callable attributes. The highly simplified code below should explain more clearly.

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.

In reality I have many methods of the parent class which must be callable. I realize that I could manually override each one for the list-like subclass, but I would like to avoid that since more methods may be added to the parent class in the future and I would like a dynamic system. Any advice or suggestions is appreciated. Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

云柯 2024-12-09 10:05:57

这段代码很糟糕,而且根本不是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

显式优于隐式一样。而且组合通常比继承更好。

This code is awful and really not Pythonic at all. There's no way for you to pass extra argument in the __getattribute__, so you shouldn't try to do any implicit magic like this. It would be better written like this:

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)

If you need, you can make the wrapper more generic, like

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

Explicit is better than implicit. Also composition is usually better than inheritance.

单身情人 2024-12-09 10:05:57

这里有几个令人困惑的地方:

1) __getattribute__ 拦截所有属性访问,这不是您想要的。您只希望代码在实际属性不存在时介入,因此您需要 __getattr__

2)您的 __getattribute__ 正在调用列表元素上的方法,但它不应该做真正的工作,它应该只返回一个可调用的东西。请记住,在 Python 中,xm(a) 实际上是两个步骤:首先,获取 xm,然后使用 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< 派生的/code>,因为它的所有产品性都是从列表元素的委托中获得的。不要使用 in dir() 来确定某个事物是否具有属性,而应使用 hasattr

You have a few points of confusion here:

1) __getattribute__ intercepts all attribute access, which isn't what you want. You only want your code to step in if a real attribute doesn't exist, so you want __getattr__.

2) Your __getattribute__ is calling the method on the list elements, but it shouldn't be doing real work, it should only return a callable thing. Remember, in Python, x.m(a) is really two steps: first, get x.m, then call that thing with an argument of a. Your function should only be doing the first step, not both steps.

3) I'm surprised that all the methods you need to override should be summed. Are there really that many methods, that really all should be summed, to make this worthwhile?

This code works to do what you want, but you might want to consider more explicit approaches, as others suggest:

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

Things to note in this code: I've made Package not derive from Product, because all of its Product-ness it gets from delegation to the elements of the list. Don't use in dir() to decide if a thing has an attribute, use hasattr.

梦途 2024-12-09 10:05:57

为了回答您眼前的问题,您可以像调用任何函数一样调用使用 getattr() 检索的函数或方法:将参数(如果有)放在函数引用后面的括号中。事实上,对函数的引用来自 getattr() 而不是属性访问,这没有任何区别。

func   = getattr(product, name)
result = func(arg)

可以将它们组合起来并消除临时变量 func

getattr(product, name)(arg)

To answer your immediate question, you call a function or method retrieved using getattr() the same way you call any function: by putting the arguments, if any, in parentheses following the reference to the function. The fact that the reference to the function comes from getattr() rather than an attribute access doesn't make any difference.

func   = getattr(product, name)
result = func(arg)

These can be combined and the temporary variable func eliminated:

getattr(product, name)(arg)
还不是爱你 2024-12-09 10:05:57

除了 Cat Plus Plus 所说的之外,如果你确实想调用魔法(请不要!在实践中使用这种方法会带来令人难以置信的许多令人不安的惊喜等待着你),你可以测试该属性是否存在于产品类,并动态创建一个 sum_with 包装器:

def __getattribute__(self, attr):
  return (
    lambda *args: self.sum_with(attr, *args) 
    if hasattr(Product, attr)
    else super(Package, self).__getattribute__(attr)
  )

In addition to what Cat Plus Plus said, if you really want to invoke magic anyway (please don't! There are unbelievably many disturbing surprises awaiting you with such an approach in practice), you could test for the presence of the attribute in the Product class, and create a sum_with wrapper dynamically:

def __getattribute__(self, attr):
  return (
    lambda *args: self.sum_with(attr, *args) 
    if hasattr(Product, attr)
    else super(Package, self).__getattribute__(attr)
  )
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文