95220


子クラスで親クラスに一部メソッドを許可し、ほかを不許可としたいです。

[code]

    class A():
      def a(self):
        print('a')
    
     def b(self):
       print('b')
    
    class B(A):
      def b(self):
        super().b()
        print('bb')
    
[/code]

このようなコードの場合、class Bのインスタンスでは、メソッドaを呼び出せないようにしたい。  
なお、既定のパッケージから継承するので、メソッド名に `__` をつけて回避する方法がとれません。


CC BY-SA 4.0
2023-06-13T08:24:51.727
95202
2023-06-16T22:12:23.627
2023-06-16T22:12:23.627
3054
32891
question
python python3 class
Pythonの親クラスにあるメソッドの一部を継承しない方法


基底クラスを変更できない場合、pythonで完全に継承元メソッドを隠ぺいすることはできないと思います。  
ただしコメントのリンク先にある方法など、対応方法はいくつか考えられます。

### raise 実行時エラー

[How to perform partial
inheritance](https://stackoverflow.com/a/11204743)の回答にあるように、関数呼び出しで例外を発生させれば有無を言わせずに関数呼び出しが中断させられます。  
下記のコードでは class B の a関数に`raise NotImplementedError`を記述しています。

[code]

    class A():
      def a(self):
        print('a')
    
      def b(self):
        print('b')
    
    class B(A):
      def a(self):
        raise NotImplementedError
    
      def b(self):
        super().b()
        print('bb')
    
    a1 = A()
    a1.a()
    b1 = B()
    b1.a()
    print("done.")
    
[/code]

**例外の例**

[code]

    例外が発生しました: NotImplementedError
    exception: no description
      File "C:\hoge.py", line 10, in a
        raise NotImplementedError
      File "C:\hoge.py", line 19, in <module>
        b1.a()
    NotImplementedError: 
    
[/code]

### debtcollector 実行時警告

関数を呼び出した時に、エラーを出して強制終了するほどではないけれど警告を出したい場合は、[debtcollector](https://pypi.org/project/debtcollector/)パッケージを導入するときれいに警告を記述できます。  
下記のコードでは class B の a関数の上に `@removals.remove`デコレータを記述し、`b1.a()`を実行した時に警告を表示します。

[code]

    from debtcollector import removals
    
    class A():
      def a(self):
        print('a')
    
      def b(self):
        print('b')
    
    class B(A):
      @removals.remove(message="Bクラスのa関数は使用禁止です")
      def a(self):
        super().a()
        print('aa')  
    
      def b(self):
        super().b()
        print('bb')
    
    a1 = A()
    a1.a()
    b1 = B()
    b1.a()
    print("done.")
    
[/code]

**出力例**

[code]

    a
    C:\hoge.py:23: DeprecationWarning: Using function/method 'B.a()' is deprecated: Bクラスのa関数は使用禁止です
      b1.a()
    a
    aa
    done.
    
[/code]

### Facadeパターンもどきでエラー

どうしてもBクラスのa関数を呼び出したくないというか、むしろBクラスはb関数しか呼び出したくないなど、呼び出す関数やプロパティが非常に少ないならば、呼び出しを限定する窓口クラスに処理を移譲する方法があります。  
下記のコードでは class Bdash が class B を内部的に保持して、 b関数のみ公開しています。

[code]

    class A():
      def a(self):
        print('a')
    
      def b(self):
        print('b')
    
    class B(A):
      def b(self):
        super().b()
        print('bb')
    
    class Bdash:
      def __init__(self):
        self.b1 = B()
    
      def b(self):
        self.b1.b()
    
    a1 = A()
    a1.a()
    b1 = Bdash()
    b1.a()
    print("done.")
    
[/code]

**例外の例**

[code]

    例外が発生しました: AttributeError
    'Bdash' object has no attribute 'a'
      File "C:\hoge.py", line 26, in <module>
        b1.a()
        ^^^^
    AttributeError: 'Bdash' object has no attribute 'a'
    
[/code]


CC BY-SA 4.0
2023-06-14T02:12:56.020
95220
2023-06-14T02:12:56.020
9820
95202
answer


### 型で呼び出し不可を表現する方法

型ヒントの付与でメソッドの呼び出しを禁止する方法も有ります。例えば[`Never`](https://docs.python.org/ja/3/library/typing.html#typing.Never)です。

[code]

    from typing import Never, NoReturn
    #from warnings import warn
    
    
    def a(self: Never) -> NoReturn:  # type: ignore
        # warn("This method is deprecated.", DeprecationWarning)
        raise RuntimeError("This method is deprecated.")
    
[/code]

これで、Mypy や Pyright などでの実行前の検査でエラーになります。Pythonのバージョンが古く、`Never`
が無い場合は適当な型を自作しても良いですし、`NoReturn`
だけでもある程度機能します。要するに、呼び出し箇所と型の不一致を発生させれば何でもよいです。

もちろん、型検査をするにしろ、実行時に警告や例外を出す処理も有った方がよいと思います。


CC BY-SA 4.0
2023-06-16T22:10:57.617
95269
2023-06-16T22:10:57.617
3054
95202
answer