web-dev-qa-db-ja.com

Pythonのクラス内からクラスメソッドにアクセスするにはどうすればよいですか?

すべての静的メンバーを管理するPythonでクラスを作成したいと思います。これらのメンバーは、クラスの定義中に既に初期化する必要があります。再初期化する必要があるため、静的メンバーは後でこのコードをクラスメソッドに配置します。

私の質問:クラス内からこのクラスメソッドを呼び出すにはどうすればよいですか?

class Test():
    # static member
    x = None
    # HERE I WOULD LOVE TO CALL SOMEHOW static_init!

    # initialize static member in classmethod, so that it can be 
    #reinitialized later on again    
    @classmethod
    def static_init(cls):
        cls.x = 10

どんな助けでもありがたいです!

よろしくお願いします、Volker

27
Volker

その時x=10はあなたの例で実行され、クラスが存在しないだけでなく、クラスメソッドも存在しません。

Pythonでの実行は上から下になります。もしx=10はclassmethodの上にあります。まだ定義されていないため、その時点ではclassmethodにアクセスできません。

Classmethodを実行できたとしても、クラスはまだ存在しないため、classmethodがそれを参照できなかったため、問題にはなりません。クラスは、クラスブロック全体が実行されるまで作成されないため、クラスブロック内にいる間はクラスがありません。

後で説明する方法で再実行できるようにクラスの初期化を除外する場合は、クラスデコレータを使用します。クラスデコレータはクラスの作成後に実行されるため、classmethodを正常に呼び出すことができます。

>>> def deco(cls):
...     cls.initStuff()
...     return cls
>>> @deco
... class Foo(object):
...     x = 10
...     
...     @classmethod
...     def initStuff(cls):
...         cls.x = 88
>>> Foo.x
88
>>> Foo.x = 10
>>> Foo.x
10
>>> Foo.initStuff() # reinitialize
>>> Foo.x
88
21
BrenBarn

同様にクラス名を追加して、クラスメソッドを呼び出します。

_class.method
_

あなたのコードでは次のようなもので十分です:

_Test.static_init()
_

これを行うこともできます:

_static_init(Test)
_

クラス内で呼び出す場合、コードでこれを実行します。

_Test.static_init()
_

私の作業コード:

_class Test(object):

    @classmethod
    def static_method(cls):
        print("Hello")

    def another_method(self):
        Test.static_method()
_

Test().another_method()Helloを返します

5
NlightNFotis

クラスがまだ完全に定義されていないため、a classmethodin the classdefinitionを呼び出すことはできません。そのため、最初のclsargument ...クラシックなチキンとしてメソッドを渡す必要はありません-と卵の問題。ただし、メタクラスのthe__new__() methodをオーバーロードし、以下に示すようにクラスが作成された後にそこからclassmethodを呼び出すことで、この制限を回避できます。

class Test(object):
    # nested metaclass definition
    class __metaclass__(type):
        def __new__(mcl, classname, bases, classdict):
            cls = type.__new__(mcl, classname, bases, classdict)  # creates class
            cls.static_init()  # call the classmethod
            return cls

    x = None

    @classmethod
    def static_init(cls):  # called by metaclass when class is defined
        print("Hello")
        cls.x = 10

print Test.x

出力:

Hello
10
3
martineau

あなたの質問をもう一度読んだ後慎重に今回は2つの解決策を考えることができます。 1つ目は、ボーグデザインパターンを適用することです。 2つ目は、クラスメソッドを破棄し、代わりにモジュールレベルの関数を使用することです。これはあなたの問題を解決するようです:

def _test_static_init(value):
    return value, value * 2

class Test:
    x, y = _test_static_init(20)

if __name__ == "__main__":
    print Test.x, Test.y

古い、間違った答え:

ここに例があります、それが役に立てば幸いです:

class Test:
    x = None

    @classmethod
    def set_x_class(cls, value):
        Test.x = value

    def set_x_self(self):
        self.__class__.set_x_class(10)

if __name__ == "__main__":
    obj = Test()
    print Test.x
    obj.set_x_self()
    print Test.x
    obj.__class__.set_x_class(15)
    print Test.x

とにかく、NlightNFotisの答えはより良いものです。クラスメソッドにアクセスするときはクラス名を使用してください。これにより、コードがわかりやすくなります。

1
ZalewaPL

これは合理的な解決策のようです:

from __future__ import annotations
from typing import ClassVar, Dict
import abc
import string


class Cipher(abc.ABC):
    @abc.abstractmethod
    def encrypt(self, plaintext: str) -> str:
        pass

    @abc.abstractmethod
    def decrypt(self, ciphertext: str) -> str:
        pass


class RotateCipher(Cipher, abc.ABC):
    @staticmethod
    def rotate(n: int) -> str:
        return string.ascii_uppercase[n:] + string.ascii_uppercase[:n]


class VigenereCipher(RotateCipher):
    _TABLE: ClassVar[Dict[str, str]] = dict({(chr(i + ord("A")), RotateCipher.rotate(i)) for i in range(26)})

    def encrypt(self, plaintext: str) -> str:
        pass

    def decrypt(self, plaintext: str) -> str:
        pass


vc = VigenereCipher()

メソッドは暗号の静的メソッドになり、クラスの外部は参照されません。 RotateCipher_RotateCipher代わりに、人々がそれを単独で使用したくない場合。

注:Finalを削除しました。これは3.7で実行したためですが、Finalのドキュメントを読んだ後、ソリューションに影響を与えるとは思いませんか?質問が欠落していたstringのインポートも追加しました。そして最後に、抽象メソッドの実装を追加しました。あるいは、VigenereCipherがabc.ABC 同様に。

0
Grismar