10

カスタム クラスのリストとして機能するサブクラスを作成しようとしています。ただし、リストが親クラスのメソッドと属性を継承し、各アイテムの数量の合計を返すようにしたいと考えています。メソッドを使用してこれを実行しようとしています__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.

実際には、呼び出し可能でなければならない親クラスのメソッドがたくさんあります。リストのようなサブクラスのそれぞれを手動でオーバーライドできることはわかっていますが、将来、親クラスにさらにメソッドが追加される可能性があり、動的システムが必要なため、それは避けたいと思います。アドバイスや提案をいただければ幸いです。ありがとう!

4

4 に答える 4

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

明示的は暗黙的よりも優れています。また、通常、構成は継承よりも優れています。

于 2011-08-30T18:28:08.980 に答える
9

ここにはいくつかの混乱点があります。

1)__getattribute__すべての属性アクセスを傍受しますが、これはあなたが望むものではありません。実際の属性が存在しない場合にのみコードを介入させたいので、__getattr__.

2)__getattribute__リスト要素でメソッドを呼び出していますが、実際の作業を行うべきではなく、呼び出し可能なもののみを返す必要があります。Python では、x.m(a)実際には 2 つのステップで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

于 2011-08-30T18:26:21.290 に答える
4

差し迫った質問に答えるには、関数を呼び出すgetattr()のと同じ方法で取得した関数またはメソッドを呼び出します。引数がある場合は、関数への参照に続いて括弧で囲みます。getattr()属性アクセスではなく関数への参照が由来するという事実は、何の違いもありません。

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

これらを組み合わせて、一時変数funcを削除できます。

getattr(product, name)(arg)
于 2011-08-30T18:26:32.460 に答える
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)
  )
于 2011-08-30T20:04:11.120 に答える