サブクラスがありますnot基本クラスにあるクラス属性を含めます。
私はこれを試しましたが、うまくいきません:
>>> class A(object):
... x = 5
>>> class B(A):
... del x
Traceback (most recent call last):
File "<pyshell#1>", line 1, in <module>
class B(A):
File "<pyshell#1>", line 2, in B
del x
NameError: name 'x' is not defined
これどうやってするの?
なぜこれを実行するのかを慎重に検討してください。あなたはおそらくしません。 BがAから継承しないようにすることを検討してください。
サブクラス化のアイデアは、オブジェクトを特殊化することです。特に、クラスの子は親クラスの有効なインスタンスである必要があります。
_>>> class foo(dict): pass
>>> isinstance(foo(), dict)
... True
_
この動作を実装する場合(例:x = property(lambda: AttributeError)
)、サブクラス化の概念に違反しており、これは悪いことです。
delattr(class, field_name)
を使用して、クラス定義から削除できます。
完全な例:
# python 3
class A:
def __init__(self):
self.field_to_keep = "keep this in B"
self.field_to_delete = "don't want this in B"
class B(A):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
delattr(self, 'field_to_delete')
削除する必要はありません。オーバーライドするだけです。
class B(A):
x = None
または単にそれを参照しないでください。
または、別のデザイン(インスタンス属性?)を検討してください。
答えはどれもうまくいきませんでした。
たとえば、delattr(SubClass, "attrname")
(またはそれに相当する_del SubClass.attrname
_)は、親メソッドを「非表示」にしません。これは、メソッド解決が機能しないためです。サブクラスにはattrname
がないため、代わりにAttributeError('attrname',)
で失敗します。そしてもちろん、attributeをNone
で置き換えても実際には削除されません。
この基本クラスを考えてみましょう:
_class Spam(object):
# Also try with `expect = True` and with a `@property` decorator
def expect(self):
return "This is pretty much expected"
_
私はそれをサブクラス化する2つの唯一の方法を知っており、expect
属性を隠しています:
___get__
_ からAttributeError
を上げる記述子クラスを使用します。属性のルックアップでは、例外が発生しますが、通常はルックアップの失敗と区別がつきません。
最も簡単な方法は、AttributeError
を発生させるプロパティを宣言することです。これは本質的に@JBernardoが示唆していたことです。
_class SpanishInquisition(Spam):
@property
def expect(self):
raise AttributeError("Nobody expects the Spanish Inquisition!")
assert hasattr(Spam, "expect") == True
# assert hasattr(SpanishInquisition, "expect") == False # Fails!
assert hasattr(SpanishInquisition(), "expect") == False
_
ただし、これはインスタンスに対してのみ機能し、クラスに対しては機能しません(hasattr(SpanishInquisition, "expect") == True
アサーションは無効になります)。
上記のすべてのアサーションをtrueにしたい場合は、これを使用します。
_class AttributeHider(object):
def __get__(self, instance, owner):
raise AttributeError("This is not the attribute you're looking for")
class SpanishInquisition(Spam):
expect = AttributeHider()
assert hasattr(Spam, "expect") == True
assert hasattr(SpanishInquisition, "expect") == False # Works!
assert hasattr(SpanishInquisition(), "expect") == False
_
コードは明確で汎用的でコンパクトなので、これは最もエレガントな方法だと思います。もちろん、本当に属性を削除することが彼らが本当に望んでいるものであるなら、二度考えるべきです。
属性ルックアップのオーバーライド ___getattribute__
_マジックメソッドを使用 。これは、サブクラス(または以下の例のように1回だけ記述したいため、ミックスイン)で実行でき、サブクラスinstancesの属性を非表示にします。メソッドをサブクラスからも非表示にする場合は、メタクラスを使用する必要があります。
_class ExpectMethodHider(object):
def __getattribute__(self, name):
if name == "expect":
raise AttributeError("Nobody expects the Spanish Inquisition!")
return super().__getattribute__(name)
class ExpectMethodHidingMetaclass(ExpectMethodHider, type):
pass
# I've used Python 3.x here, thus the syntax.
# For Python 2.x use __metaclass__ = ExpectMethodHidingMetaclass
class SpanishInquisition(ExpectMethodHider, Spam,
metaclass=ExpectMethodHidingMetaclass):
pass
assert hasattr(Spam, "expect") == True
assert hasattr(SpanishInquisition, "expect") == False
assert hasattr(SpanishInquisition(), "expect") == False
_
これは上記の方法よりも悪く見えますが(冗長で一般的ではありません)、この方法も検討できます。
これらは___len__
_をバイパスするため、これは特別な(「マジック」)メソッド(たとえば___getproperty__
_)に対してnotを機能させることに注意してください。詳細については、Pythonのドキュメントの 特別なメソッドルックアップ セクションをご覧ください。これを元に戻す必要がある場合は、オーバーライドしてobject
'sを呼び出します。実装、親をスキップします。
言うまでもなく、これは「新しいスタイルのクラス」(object
から継承するクラス)にのみ適用されます。魔法のメソッドと記述子プロトコルはそこでサポートされていないためです。うまくいけば、それらは過去のものです。
たぶん、あなたはx
をproperty
として設定し、誰かがアクセスしようとするたびにAttributeErrorを発生させることができます。
>>> class C:
x = 5
>>> class D(C):
def foo(self):
raise AttributeError
x = property(foo)
>>> d = D()
>>> print(d.x)
File "<pyshell#17>", line 3, in foo
raise AttributeError
AttributeError
私も同じ問題を抱えていて、サブクラスのクラス属性を削除する正当な理由があると思いました:スーパークラス(Aと呼びます)には、属性の値を提供する読み取り専用プロパティがありましたが、私のサブクラス(Bと呼びます)では、属性は読み取り/書き込みインスタンス変数でした。 Pythonは、インスタンス変数がそれをオーバーライドする必要があると考えていたにもかかわらず、プロパティ関数を呼び出していました。基になるプロパティにアクセスするために使用する別のゲッター関数を作成することもできましたが、 (それが本当に重要であるかのように)インターフェース名前空間の不必要でエレガントでない乱雑なように見えました。
結局のところ、答えはAの元の共通属性を持つ新しい抽象スーパークラス(Sと呼ぶ)を作成し、AとBをSから派生させることでした。Python 、BがAを拡張しなくても問題ありません。暗黙的に同じインターフェイスを実装しているため、同じ場所で使用できます。
これをしようとすることはおそらく悪い考えですが、...
デフォルトでB.x
を検索する方法が原因で、「適切な」継承を介してこれを行うようには見えません。 B.x
を取得する場合、x
は最初にB
で検索され、見つからない場合はA
で検索されますが、逆にB.x
のみを設定または削除する場合はB
が検索されます。だから例えば
>>> class A:
>>> x = 5
>>> class B(A):
>>> pass
>>> B.x
5
>>> del B.x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: class B has no attribute 'x'
>>> B.x = 6
>>> B.x
6
>>> del B.x
>>> B.x
5
ここでは、最初にB.x
が存在しないため削除できないようです(A.x
が存在し、B.x
を評価するときに提供されるものです)。ただし、B.x
を6に設定すると、B.x
が存在し、B.x
によって取得され、del B.x
によって削除され、存在しなくなるため、A.x
は、B.x
への応答として提供されます。
一方、あなたがcouldすることは、メタクラスを使用してB.x
を発生させることですAttributeError
:
class NoX(type):
@property
def x(self):
raise AttributeError("We don't like X")
class A(object):
x = [42]
class B(A, metaclass=NoX):
pass
print(A.x)
print(B.x)
もちろん、純粋主義者はこれがLSPに違反すると叫ぶかもしれませんが、それほど単純ではありません。これは、これを実行してサブタイプを作成したと考えると、結局のところすべてです。 issubclass
メソッドとisinstance
メソッドは「はい」と言いますが、LSPは「いいえ」と言います(そして、多くのプログラマーはA
から継承するため、「はい」と見なします)。
LSPは、B
がA
のサブタイプである場合、B
を使用できるときはいつでもA
を使用できますが、これができないためこの構成を実行している間、B
は実際にはA
のサブタイプではないため、LSPに違反していないと結論付けることができます。