2016-03-23 10 views
3

にサブクラス化、私はこのクラスを持っていると言う:メンバーリストへの追加のPython

class FooClass(object): 
    foos = ["foo", "bar"] 

    def do_foos(self): 
     for foo in self.foos: 
      print("I have a " + foo) 

    # ... 

私は(すなわちfoosに項目"spec"を追加し、FooClassを拡張し、このクラスのSpecialisedFooClassを作成したいfoosが含まれるように、 ["foo", "bar", "spec"])。

SpecialisedFooClass.foosFooClass.foosに依存すべき:foosが含まれているので、私はFooClass "の定義を変更した場合["foo", "bam", "bat"]SpecialisedFooClass.foosは、["foo", "bam", "bat", "spec"]が含まれている必要があります。


これは私がこれまでに作ってみた最善の方法である:

class SpecialisedFooClass(FooClass): 
    foos = FooClass.foos + ["spec"] 

しかし、私はに関するFooClassへの明示的な参照を見つけます。仲介サブクラスを追加することにしたとき(つまり、SpecialisedFooClass 'スーパークラスが変更されたとき)、私は必然的にこのリファレンスを更新することを忘れてしまいます。私は実際に私が作業しているコードベース(これは実際にはのfoos、bams、およびbatsを扱わない...)でこのエラーIRLを実際に作っています。


は実際にfoosは、クラスのメンバではなく、インスタンスのメンバであることを私の場合には特別な要件はありませんので、これも動作しますが、私はそれが醜い見つけます。また、superコールには明示的なクラス参照があります。なぜなら、そのクラスに表示されるためです。

class FooClass(object): 
    def __init__(self, *args, **kwargs): 
     self.foos = ["foo", "bar"] 

    def do_foos(self): 
     for foo in self.foos: 
      print("I have a " + foo) 

class SpecialisedFooClass(FooClass): 
    def __init__(self, *args, **kwargs): 
     super(SpecialisedFooClass, self).__init__(*args, **kwargs) 
     self.foos.append("spec") 

他のどのようなオプションが存在し、これを行うには、「Python的」な方法はありますか? fooのリストを取得するためにインスタンス化する必要はありませんので、クラスメソッドに変換

class A(object): 
    foo_ = ['a'] 

    @classmethod 
    def foo(cls): 
     return sum((getattr(c, 'foo_', []) for c in cls.__mro__[::-1]), []) 

class B(A): 
    foo_ = ['b'] 

class C(B): 
    foo_ = ['c','d','e'] 

class D(A): 
    foo_ = ['f', 'g', 'h'] 

class E(B,D): 
    foo_ = ['i', 'j', 'k'] 


for cls in (A,B,C,D,E): 
    print cls.__name__, cls.foo() 

プリント

A ['a'] 
B ['a', 'b'] 
C ['a', 'b', 'c', 'd', 'e'] 
D ['a', 'f', 'g', 'h'] 
E ['a', 'f', 'g', 'h', 'b', 'i', 'j', 'k'] 

EDIT

答えて

2

これを処理できる方法はいくつかあります。一つのオプション、属性は、計算のいくつかの量を必要とする場合、代わりに返すのそれは一度だけ計算だように、あなたはいくつかの最適化を行うことができ、またはそれは実行時に変更することができるようにproperty

class FooClass(object): 
    foos = ["foo", "bar"] 


class SpecializedFooClass(FooClass) 

    @property 
    def foos(self): 
     return super(SpecializedFooClass, self).foos + ['spec'] 

に変換することです毎回同じこと。

class SpecializedFooClass(FooClass) 

    def __init__(self): 
     super(SpecializedFoo, self).__init__() 
     self._foos = None 

    @property 
    def foos(self): 
     if self._foos is None: 
      self._foos = super(SpecializedFooClass, self).foos + ['spec'] 
     return self._foos 

プロパティを使用しての主な欠点は、このような状況は、あなたが値を取得するには、クラスをインスタンス化する必要がありますので、それは本当にもう、class属性のように動作しないことです。

metaclassesgreat answer on metaclasses)を使用することもできます。場合によっては、コードベースを大幅に減らすこともできますが、継承チェーンが非常に深く、メタクラスが使用されているかどうかは分かりません。しかしプラスの面では、実際にはクラス属性なので、クラス属性とまったく同じように動作します。

class FooMeta(type): 

    def __new__(cls, name, bases, attrs): 
     foos = [] 
     for base in bases: 
      if hasattr(base, 'foos'): 
       foos.extend(base.foos) 
     attrs['foos'] = foos + attrs.get('foos', []) 
     return type.__new__(cls, name, bases, attrs) 


class Foo(object): 
    __metaclass__ = FooMeta 
    foos = ['one', 'two'] 


class SpecialFoo(Foo): 
    foos = ['three'] 

print Foo.foos 
# ['one', 'two'] 

print SpecialFoo.foos 
# ['one', 'two', 'three'] 

3番目のオプションはクラスデコレータを使用しています。これは、メタクラスより少し魔法ですが、サブクラスを飾ることを覚えておく必要があることを意味します。実際にはクラス属性のように機能します。

def set_foos(cls): 
    foos = [] 
    for base in cls.__bases__: 
     if hasattr(base, 'foos'): 
      foos.extend(base.foos) 
    cls.foos = foos + getattr(cls, 'foos', []) 
    return cls 


class FooClass(object): 
    foos = ['foo', 'bar'] 


@set_foo 
class SpecialFoo(FooClass): 
    foos = ['spec'] 
+1

メタクラスのアプローチは涼しく、それを考えなかったでしょう! – Bahrom

+1

この小さな例では、それはおそらく保証されませんが、これがちょうど氷山の先端であれば、それは意味をなさないかもしれません。 –

+0

別のオプションクラスのデコレータが追加されました。 –

2

これは、あなたは近いかもしれません。また、これが見えるようにするためにダイヤモンドの継承を追加しました。

関連する問題