Pythonでは静的クラス変数は可能ですか?


回答:


1900

メソッド内ではなくクラス定義内で宣言された変数は、クラス変数または静的変数です。

>>> class MyClass:
...     i = 3
...
>>> MyClass.i
3 

@ millerdevが指摘するように、これはクラスレベルのi変数を作成しますが、これはインスタンスレベルのi変数とは異なるため、

>>> m = MyClass()
>>> m.i = 4
>>> MyClass.i, m.i
>>> (3, 4)

これはC ++やJavaとは異なりますが、インスタンスへの参照を使用して静的メンバーにアクセスできないC#とはそれほど異なりません。

クラスとクラスオブジェクトについて、Pythonチュートリアルが何を言っているかをご覧ください。

@Steve Johnsonは、静的メソッドに関して既に回答しています。これは、Pythonライブラリリファレンスの「組み込み関数」にも記載されています。

class C:
    @staticmethod
    def f(arg1, arg2, ...): ...

メソッドが最初の引数としてクラス型を受け取るため、@ beidyは静的メソッドよりもクラスメソッドを推奨しますが、静的メソッドに対するこのアプローチの利点についてはまだ少し曖昧です。あなたもそうなら、それはおそらく問題ではありません。


11
私はPythonを学習しているだけですが、AFAIK @classmethodよりも@staticmethod優れている点は、サブクラスであっても、メソッドが呼び出されたクラスの名前を常に取得できることです。静的メソッドにはこの情報がないため、たとえばオーバーライドされたメソッドを呼び出すことはできません。
セブ

49
@theJollySin定数のpythonicの方法は、定数のクラスを拡張しないことです。いくつかconst.pyを持っているだけPI = 3.14で、どこにでもインポートできます。from const import PI
Giszmo 2013年

30
この答えは、静的変数の問題を混乱させる可能性があります。そもそも、i = 3は静的変数ではなくクラス属性であり、インスタンスレベルの属性iとは異なるため、他の言語では静的変数のように動作しませ。参照してくださいmillerdevの答えヤンの答え、そして私の答え下記を。
リックはモニカ

2
したがってi、このクラスの数百のインスタンスを作成した場合でも、(静的変数)の1つのコピーのみがメモリに存在しますか?
2017

2
Danielが@Dubslowのコメントで言及した興味のある人にとっては、millerdevウェイバックマシン)です
HeyJude

618

@Blairコンラッド氏によると、クラス定義内で宣言された静的変数はメソッド内ではなくクラスまたは「静的」変数であるとのことです。

>>> class Test(object):
...     i = 3
...
>>> Test.i
3

ここにいくつかの落とし穴があります。上記の例から続けます:

>>> t = Test()
>>> t.i     # "static" variable accessed via instance
3
>>> t.i = 5 # but if we assign to the instance ...
>>> Test.i  # we have not changed the "static" variable
3
>>> t.i     # we have overwritten Test.i on t by creating a new attribute t.i
5
>>> Test.i = 6 # to change the "static" variable we do it by assigning to the class
>>> t.i
5
>>> Test.i
6
>>> u = Test()
>>> u.i
6           # changes to t do not affect new instances of Test

# Namespaces are one honking great idea -- let's do more of those!
>>> Test.__dict__
{'i': 6, ...}
>>> t.__dict__
{'i': 5}
>>> u.__dict__
{}

t.i属性iがに直接設定されたときに、インスタンス変数が「静的」クラス変数と同期しなくなったことに注意してくださいt。これはit名前空間とは異なる名前空間内で再バインドされたためTestです。「静的」変数の値を変更する場合は、元々定義されていたスコープ(またはオブジェクト)内で変更する必要があります。PythonにはC ++やJavaのように静的変数がないので、「静的」を引用符で囲みました。

静的変数やメソッドについては何も述べていませんが、Pythonチュートリアルには、クラスとクラスオブジェクトに関するいくつかの関連情報があります

@Steve Johnsonは、静的メソッドに関しても回答しました。これも、Pythonライブラリリファレンスの「組み込み関数」に記載されています。

class Test(object):
    @staticmethod
    def f(arg1, arg2, ...):
        ...

@beidは、静的メソッドに類似したクラスメソッドにも言及しました。クラスメソッドの最初の引数はクラスオブジェクトです。例:

class Test(object):
    i = 3 # class (or static) variable
    @classmethod
    def g(cls, arg):
        # here we can use 'cls' instead of the class name (Test)
        if arg > cls.i:
            cls.i = arg # would be the same as Test.i = arg1

上記の例の画像表現


3
この例を少し拡張することをお勧めします。Test.i= 6を設定した後、新しいオブジェクト(たとえば、u = Test())をインスタンス化すると、新しいオブジェクトは新しいクラス値(たとえば、 ui == 6)
マーク

2
同期に静的変数を維持するための方法は、それらの特性を作ることです:class Test(object):_i = 3@propertydef i(self)return type(self)._i@i.setterdef i(self,val):type(self)._i = val。今、あなたが行うことができますx = Test()x.i = 12assert x.i == Test.i
リックはモニカ

1
したがって、すべての変数は最初は静的であり、インスタンスにアクセスすると実行時にインスタンス変数が作成されると言えるでしょうか?
Ali

おそらくこれは興味深いでしょう。Test.iを変更するメソッドをTestで定義すると、Test.iとtiの両方の値に影響します。
パブロ

@millerdev、先ほど触れたように、PythonにはC ++やJAVAのように静的変数はありません。だから、Test.iは静的変数ではなくクラス変数のほうがいいのでしょうか。
Tyto 2018

197

静的メソッドとクラスメソッド

他の回答が指摘しているように、静的メソッドとクラスメソッドは、組み込みのデコレータを使用して簡単に実行できます。

class Test(object):

    # regular instance method:
    def MyMethod(self):
        pass

    # class method:
    @classmethod
    def MyClassMethod(klass):
        pass

    # static method:
    @staticmethod
    def MyStaticMethod():
        pass

通常どおり、への最初の引数MyMethod()はクラスインスタンスオブジェクトにバインドされます。対照的に、への最初の引数MyClassMethod()、クラスオブジェクト自体にバインドされます(たとえば、この場合はTest)。の場合MyStaticMethod()、バインドされる引数はなく、引数を持つことはオプションです。

「静的変数」

ただし、「静的変数」(まあ、とにかく、それが用語で矛盾しない場合は可変の静的変数)を実装するのはそれほど簡単ではありません。Millerdev が彼の回答指摘したように、問題はPythonのクラス属性が本当に「静的変数」ではないことです。考慮してください:

class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i

これは、行x.i = 12がクラス属性の値を変更iするx代わりに新しいインスタンス属性を追加したためです。Testi

予期される部分的な静的変数の動作、つまり、複数のインスタンス間での属性の同期(ただし、クラス自体ではありません。以下の「問題」を参照)は、クラス属性をプロパティに変換することで実現できます。

class Test(object):

    _i = 3

    @property
    def i(self):
        return type(self)._i

    @i.setter
    def i(self,val):
        type(self)._i = val

## ALTERNATIVE IMPLEMENTATION - FUNCTIONALLY EQUIVALENT TO ABOVE ##
## (except with separate methods for getting and setting i) ##

class Test(object):

    _i = 3

    def get_i(self):
        return type(self)._i

    def set_i(self,val):
        type(self)._i = val

    i = property(get_i, set_i)

今あなたはできる:

x1 = Test()
x2 = Test()
x1.i = 50
assert x2.i == x1.i  # no error
assert x2.i == 50    # the property is synced

これで、静的変数はすべてのクラスインスタンス間で同期したままになります

(注:つまり、クラスインスタンスが独自のバージョンの_i!を定義することを決定しない限り!

技術的に言えば、iまだ「静的変数」ではないことに注意してください。それはproperty記述の特殊なタイプです。ただし、このproperty動作は、すべてのクラスインスタンス間で同期される(可変)静的変数と同等になりました。

不変の「静的変数」

不変の静的変数の動作については、単にpropertyセッターを省略します。

class Test(object):

    _i = 3

    @property
    def i(self):
        return type(self)._i

## ALTERNATIVE IMPLEMENTATION - FUNCTIONALLY EQUIVALENT TO ABOVE ##
## (except with separate methods for getting i) ##

class Test(object):

    _i = 3

    def get_i(self):
        return type(self)._i

    i = property(get_i)

インスタンスi属性を設定しようとすると、AttributeError次が返されます:

x = Test()
assert x.i == 3  # success
x.i = 12         # ERROR

知っておくべき1つの問題

上記の方法でのみ動作することに注意してくださいインスタンス彼らはなります-あなたのクラスのがない作業クラス自体を使用。だから例えば:

x = Test()
assert x.i == Test.i  # ERROR

# x.i and Test.i are two different objects:
type(Test.i)  # class 'property'
type(x.i)     # class 'int'

およびassert Test.i == x.ii属性は2つの異なるオブジェクトであるため、この行ではエラーが発生します。Testx

多くの人々はこれを意外と感じるでしょう。しかし、そうであってはなりません。前に戻ってTestクラス定義(2番目のバージョン)を調べたら、次の行に注意してください。

    i = property(get_i) 

明らかに、のメンバーiは、関数から返されるオブジェクトのタイプであるオブジェクトでTestなければなりません。propertyproperty

上記の混乱を見つけた場合でも、他の言語(Javaやc ++など)の観点から考えている可能性があります。あなたは勉強に行くべきですpropertyPython属性が返される順序、記述子プロトコル、メソッド解決順序(MRO)についてオブジェクトを。

私は上記の「問題」の解決策を以下に示します。しかし、私は-激しく--少なくとも-あなたが完全に理由を理解するまで、次のようなことをしようとしないことをお勧めしますassert Test.i = x.iエラーが発生します。

実際、実際静的変数-Test.i == x.i

以下の(Python 3)ソリューションは、情報提供のみを目的として提示しています。私はそれを「良い解決策」として保証していません。他の言語の静的変数の振る舞いをPythonでエミュレートすることが実際に必要かどうか疑問に思っています。ただし、それが実際に役立つかどうかに関係なく、Pythonがどのように機能するかをさらに理解するには、以下を参考にしてください。

更新:この試みは本当にかなりひどいです。このようなことを主張する場合(ヒント:しないでください。Pythonは非常にエレガントな言語であり、他の言語のように動作させるために靴角を張る必要はありません)、代わりにEthan Furmanの回答のコードを使用してください。

メタクラスを使用して他の言語の静的変数の動作をエミュレートする

メタクラスはクラスのクラスです。Pythonのすべてのクラスのデフォルトのメタクラス(つまり、Python 2.3以降の「新しいスタイル」クラス)はtypeです。例えば:

type(int)  # class 'type'
type(str)  # class 'type'
class Test(): pass
type(Test) # class 'type'

ただし、次のように独自のメタクラスを定義できます。

class MyMeta(type): pass

そして、それを次のようにあなた自身のクラスに適用してください(Python 3のみ):

class MyClass(metaclass = MyMeta):
    pass

type(MyClass)  # class MyMeta

以下は、他の言語の「静的変数」の動作をエミュレートしようとする、私が作成したメタクラスです。基本的には、デフォルトのゲッター、セッター、およびデリーターを、要求されている属性が「静的変数」であるかどうかを確認するバージョンに置き換えることで機能します。

「静的変数」のカタログはStaticVarMeta.statics属性に格納されます。すべての属性要求は、代替の解決順序を使用して最初に解決されます。これを「静的解決順序」または「SRO」と呼んでいます。これは、特定のクラス(またはその親クラス)の「静的変数」のセットで要求された属性を探すことによって行われます。属性が「SRO」に表示されない場合、クラスはデフォルトの属性の取得/設定/削除動作(つまり、「MRO」)にフォールバックします。

from functools import wraps

class StaticVarsMeta(type):
    '''A metaclass for creating classes that emulate the "static variable" behavior
    of other languages. I do not advise actually using this for anything!!!

    Behavior is intended to be similar to classes that use __slots__. However, "normal"
    attributes and __statics___ can coexist (unlike with __slots__). 

    Example usage: 

        class MyBaseClass(metaclass = StaticVarsMeta):
            __statics__ = {'a','b','c'}
            i = 0  # regular attribute
            a = 1  # static var defined (optional)

        class MyParentClass(MyBaseClass):
            __statics__ = {'d','e','f'}
            j = 2              # regular attribute
            d, e, f = 3, 4, 5  # Static vars
            a, b, c = 6, 7, 8  # Static vars (inherited from MyBaseClass, defined/re-defined here)

        class MyChildClass(MyParentClass):
            __statics__ = {'a','b','c'}
            j = 2  # regular attribute (redefines j from MyParentClass)
            d, e, f = 9, 10, 11   # Static vars (inherited from MyParentClass, redefined here)
            a, b, c = 12, 13, 14  # Static vars (overriding previous definition in MyParentClass here)'''
    statics = {}
    def __new__(mcls, name, bases, namespace):
        # Get the class object
        cls = super().__new__(mcls, name, bases, namespace)
        # Establish the "statics resolution order"
        cls.__sro__ = tuple(c for c in cls.__mro__ if isinstance(c,mcls))

        # Replace class getter, setter, and deleter for instance attributes
        cls.__getattribute__ = StaticVarsMeta.__inst_getattribute__(cls, cls.__getattribute__)
        cls.__setattr__ = StaticVarsMeta.__inst_setattr__(cls, cls.__setattr__)
        cls.__delattr__ = StaticVarsMeta.__inst_delattr__(cls, cls.__delattr__)
        # Store the list of static variables for the class object
        # This list is permanent and cannot be changed, similar to __slots__
        try:
            mcls.statics[cls] = getattr(cls,'__statics__')
        except AttributeError:
            mcls.statics[cls] = namespace['__statics__'] = set() # No static vars provided
        # Check and make sure the statics var names are strings
        if any(not isinstance(static,str) for static in mcls.statics[cls]):
            typ = dict(zip((not isinstance(static,str) for static in mcls.statics[cls]), map(type,mcls.statics[cls])))[True].__name__
            raise TypeError('__statics__ items must be strings, not {0}'.format(typ))
        # Move any previously existing, not overridden statics to the static var parent class(es)
        if len(cls.__sro__) > 1:
            for attr,value in namespace.items():
                if attr not in StaticVarsMeta.statics[cls] and attr != ['__statics__']:
                    for c in cls.__sro__[1:]:
                        if attr in StaticVarsMeta.statics[c]:
                            setattr(c,attr,value)
                            delattr(cls,attr)
        return cls
    def __inst_getattribute__(self, orig_getattribute):
        '''Replaces the class __getattribute__'''
        @wraps(orig_getattribute)
        def wrapper(self, attr):
            if StaticVarsMeta.is_static(type(self),attr):
                return StaticVarsMeta.__getstatic__(type(self),attr)
            else:
                return orig_getattribute(self, attr)
        return wrapper
    def __inst_setattr__(self, orig_setattribute):
        '''Replaces the class __setattr__'''
        @wraps(orig_setattribute)
        def wrapper(self, attr, value):
            if StaticVarsMeta.is_static(type(self),attr):
                StaticVarsMeta.__setstatic__(type(self),attr, value)
            else:
                orig_setattribute(self, attr, value)
        return wrapper
    def __inst_delattr__(self, orig_delattribute):
        '''Replaces the class __delattr__'''
        @wraps(orig_delattribute)
        def wrapper(self, attr):
            if StaticVarsMeta.is_static(type(self),attr):
                StaticVarsMeta.__delstatic__(type(self),attr)
            else:
                orig_delattribute(self, attr)
        return wrapper
    def __getstatic__(cls,attr):
        '''Static variable getter'''
        for c in cls.__sro__:
            if attr in StaticVarsMeta.statics[c]:
                try:
                    return getattr(c,attr)
                except AttributeError:
                    pass
        raise AttributeError(cls.__name__ + " object has no attribute '{0}'".format(attr))
    def __setstatic__(cls,attr,value):
        '''Static variable setter'''
        for c in cls.__sro__:
            if attr in StaticVarsMeta.statics[c]:
                setattr(c,attr,value)
                break
    def __delstatic__(cls,attr):
        '''Static variable deleter'''
        for c in cls.__sro__:
            if attr in StaticVarsMeta.statics[c]:
                try:
                    delattr(c,attr)
                    break
                except AttributeError:
                    pass
        raise AttributeError(cls.__name__ + " object has no attribute '{0}'".format(attr))
    def __delattr__(cls,attr):
        '''Prevent __sro__ attribute from deletion'''
        if attr == '__sro__':
            raise AttributeError('readonly attribute')
        super().__delattr__(attr)
    def is_static(cls,attr):
        '''Returns True if an attribute is a static variable of any class in the __sro__'''
        if any(attr in StaticVarsMeta.statics[c] for c in cls.__sro__):
            return True
        return False

私はあなたの方法を使おうとしましたが、私は問題に直面しました、親切に私の質問をここで見てくださいstackoverflow.com/questions/29329850/get-static-variable-value
Muhammed Refaat

@RickTeachey:クラスインスタンスTest(インスタンスをインスタンス化するために使用する前に)で行うことは一般に、メタプログラミングの領域にあるものとして表示する必要があると思いますか?たとえば、クラスの動作を変更して変更しますTest.i = 0(ここでは、プロパティオブジェクトを完全に破棄するだけです)。「プロパティメカニズム」は、クラスのインスタンスのプロパティアクセスでのみ発生すると思います(メタクラスを中間として使用して基本的な動作を変更しない限り)。ところで、この回答を終了してください:-)
Ole Thomsen Buus

1
@RickTeacheyありがとう:-)結局のところ、メタクラスは興味深いですが、私の好みでは実際には少し複雑すぎます。このメカニズムが絶対に必要な大規模なフレームワーク/アプリケーションで役立つ場合があります。とにかく、これは、新しい(複雑な)非デフォルトのメタ動作が本当に必要な場合にPythonがそれを可能にすることを例示しています:)
Ole Thomsen Buus

1
@OleThomsenBuus:私の答えをチェックして、その仕事を行うより簡単なメタクラスを探してください。
イーサンファーマン

1
@taperあなたは正しいです。私は問題を修正するために答えを編集しました(それが長い間そこに間違って置かれているとは信じられません!)。混乱させて申し訳ありません。
リックはモニカ

33

クラス変数をその場でクラスに追加することもできます

>>> class X:
...     pass
... 
>>> X.bar = 0
>>> x = X()
>>> x.bar
0
>>> x.foo
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
AttributeError: X instance has no attribute 'foo'
>>> X.foo = 1
>>> x.foo
1

そしてクラスインスタンスはクラス変数を変更できます

class X:
  l = []
  def __init__(self):
    self.l.append(1)

print X().l
print X().l

>python test.py
[1]
[1, 1]

3
クラスが別のモジュールにインポートされても、新しいクラス変数は保持されますか?
zakdances 2013年

はい。クラスは、呼び出し元の名前空間に関係なく、事実上シングルトンです。
ペドロ

@Gregoryは、「クラスインスタンスはクラス変数を変更できる」と述べましたが、実際にはこの例は変更ではなくアクセスと呼ばれています。変更は、独自のappend()関数を介してオブジェクト自体によって行われました。
Amr ALHOSSARY

19

個人的には、静的メソッドが必要なときはいつでもクラスメソッドを使用していました。主に、引数としてクラスを取得するためです。

class myObj(object):
   def myMethod(cls)
     ...
   myMethod = classmethod(myMethod) 

またはデコレータを使用する

class myObj(object):
   @classmethod
   def myMethod(cls)

静的プロパティの場合..そのときは、いくつかのpython定義を調べます。変数は常に変更できます。それらには、変更可能と不変の2つのタイプがあります。また、クラス属性とインスタンス属性があります。

クラスと何の関係もないのに、なぜ静的メソッドをPythonicの意味で使用するのですか?私があなたなら、classmethodを使用するか、クラスから独立したメソッドを定義します。


1
変数は変更可能または不変ではありません。オブジェクトがあります。(ただし、オブジェクトは、成功の度合いが異なる場合、特定の属性への割り当てを回避しようとすることができます。)
Davis Herring

JavaとC ++は、インスタンス属性とクラス属性を使用する場合とまったく同じように、静的(imhoという単語を使用)を使用します。クラス属性/メソッドはJavaとC ++では静的ですが、Pythonではクラスメソッド呼び出しの最初のパラメーターがクラスであることを除いて、違いはありません。
Angel O'Sphere

16

次の例に示すように、静的プロパティとインスタンスプロパティについて特筆すべき点は次のとおりです。

class my_cls:
  my_prop = 0

#static property
print my_cls.my_prop  #--> 0

#assign value to static property
my_cls.my_prop = 1 
print my_cls.my_prop  #--> 1

#access static property thru' instance
my_inst = my_cls()
print my_inst.my_prop #--> 1

#instance property is different from static property 
#after being assigned a value
my_inst.my_prop = 2
print my_cls.my_prop  #--> 1
print my_inst.my_prop #--> 2

つまり、インスタンスプロパティに値を割り当てる前に、インスタンスを通じてプロパティにアクセスしようとすると、静的な値が使用されます。Pythonクラスで宣言された各プロパティは、常にメモリ内に静的スロットを持っています


16

Pythonの静的メソッドはclassmethodと呼ばれます。次のコードを見てください

class MyClass:

    def myInstanceMethod(self):
        print 'output from an instance method'

    @classmethod
    def myStaticMethod(cls):
        print 'output from a static method'

>>> MyClass.myInstanceMethod()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method myInstanceMethod() must be called [...]

>>> MyClass.myStaticMethod()
output from a static method

メソッドmyInstanceMethodを呼び出すと、エラーが発生することに注意してください。これは、このクラスのインスタンスでメソッドを呼び出す必要があるためです。メソッドmyStaticMethodは、デコレータ @classmethodを使用してクラスメソッドとして設定されます。

キックとクスクスのために、クラスのインスタンスを渡すことにより、クラスでmyInstanceMethodを呼び出すことができます。

>>> MyClass.myInstanceMethod(MyClass())
output from an instance method

2
うーん...静的メソッドは@staticmethod; @classmethod(明らかに)クラスメソッド用です(これは主に代替コンストラクタとしての使用を目的としていますが、たまたま呼び出されたクラスへの参照を受け取る静的メソッドとしてピンチで機能します)。
ShadowRanger

11

メンバーメソッドの外部でメンバー変数を定義する場合、変数の表現方法に応じて、変数は静的または非静的のいずれかになります。

  • CLASSNAME.varは静的変数です
  • INSTANCENAME.varは静的変数ではありません。
  • クラス内のself.varは静的変数ではありません。
  • クラスメンバー関数内のvarが定義されていません。

例えば:

#!/usr/bin/python

class A:
    var=1

    def printvar(self):
        print "self.var is %d" % self.var
        print "A.var is %d" % A.var


    a = A()
    a.var = 2
    a.printvar()

    A.var = 3
    a.printvar()

結果は

self.var is 2
A.var is 1
self.var is 2
A.var is 3

インデントが壊れています。これは実行されません
Thomas Weller

9

staticクラス変数を持つことは可能ですが、おそらく努力する価値はありません。

Python 3で記述された概念実証は次のとおりです。正確な詳細のいずれかが間違っている場合、コードを微調整して、あなたが何を意味しているのかほぼ一致するようにできますstatic variable


class Static:
    def __init__(self, value, doc=None):
        self.deleted = False
        self.value = value
        self.__doc__ = doc
    def __get__(self, inst, cls=None):
        if self.deleted:
            raise AttributeError('Attribute not set')
        return self.value
    def __set__(self, inst, value):
        self.deleted = False
        self.value = value
    def __delete__(self, inst):
        self.deleted = True

class StaticType(type):
    def __delattr__(cls, name):
        obj = cls.__dict__.get(name)
        if isinstance(obj, Static):
            obj.__delete__(name)
        else:
            super(StaticType, cls).__delattr__(name)
    def __getattribute__(cls, *args):
        obj = super(StaticType, cls).__getattribute__(*args)
        if isinstance(obj, Static):
            obj = obj.__get__(cls, cls.__class__)
        return obj
    def __setattr__(cls, name, val):
        # check if object already exists
        obj = cls.__dict__.get(name)
        if isinstance(obj, Static):
            obj.__set__(name, val)
        else:
            super(StaticType, cls).__setattr__(name, val)

そして使用中:

class MyStatic(metaclass=StaticType):
    """
    Testing static vars
    """
    a = Static(9)
    b = Static(12)
    c = 3

class YourStatic(MyStatic):
    d = Static('woo hoo')
    e = Static('doo wop')

そしていくつかのテスト:

ms1 = MyStatic()
ms2 = MyStatic()
ms3 = MyStatic()
assert ms1.a == ms2.a == ms3.a == MyStatic.a
assert ms1.b == ms2.b == ms3.b == MyStatic.b
assert ms1.c == ms2.c == ms3.c == MyStatic.c
ms1.a = 77
assert ms1.a == ms2.a == ms3.a == MyStatic.a
ms2.b = 99
assert ms1.b == ms2.b == ms3.b == MyStatic.b
MyStatic.a = 101
assert ms1.a == ms2.a == ms3.a == MyStatic.a
MyStatic.b = 139
assert ms1.b == ms2.b == ms3.b == MyStatic.b
del MyStatic.b
for inst in (ms1, ms2, ms3):
    try:
        getattr(inst, 'b')
    except AttributeError:
        pass
    else:
        print('AttributeError not raised on %r' % attr)
ms1.c = 13
ms2.c = 17
ms3.c = 19
assert ms1.c == 13
assert ms2.c == 17
assert ms3.c == 19
MyStatic.c = 43
assert ms1.c == 13
assert ms2.c == 17
assert ms3.c == 19

ys1 = YourStatic()
ys2 = YourStatic()
ys3 = YourStatic()
MyStatic.b = 'burgler'
assert ys1.a == ys2.a == ys3.a == YourStatic.a == MyStatic.a
assert ys1.b == ys2.b == ys3.b == YourStatic.b == MyStatic.b
assert ys1.d == ys2.d == ys3.d == YourStatic.d
assert ys1.e == ys2.e == ys3.e == YourStatic.e
ys1.a = 'blah'
assert ys1.a == ys2.a == ys3.a == YourStatic.a == MyStatic.a
ys2.b = 'kelp'
assert ys1.b == ys2.b == ys3.b == YourStatic.b == MyStatic.b
ys1.d = 'fee'
assert ys1.d == ys2.d == ys3.d == YourStatic.d
ys2.e = 'fie'
assert ys1.e == ys2.e == ys3.e == YourStatic.e
MyStatic.a = 'aargh'
assert ys1.a == ys2.a == ys3.a == YourStatic.a == MyStatic.a

8

メタクラスを使用して、クラスを静的にすることもできます。

class StaticClassError(Exception):
    pass


class StaticClass:
    __metaclass__ = abc.ABCMeta

    def __new__(cls, *args, **kw):
        raise StaticClassError("%s is a static class and cannot be initiated."
                                % cls)

class MyClass(StaticClass):
    a = 1
    b = 3

    @staticmethod
    def add(x, y):
        return x+y

次に、誤ってMyClassを初期化しようとすると、StaticClassErrorが発生します。


4
インスタンス化しないのであれば、なぜクラスでさえあるのでしょうか?このJavaにそれを回すためにはPythonをねじるように感じている....
ネッドBatchelder

1
ボルグイディオムは、これを処理するためのより良い方法です。
Rickは15:03にMonica

@NedBatchelderこれは、サブクラス化(およびサブクラスのインスタンス化)のみを目的とした抽象クラスです
stevepastelan

1
サブクラス__new__がその親のを呼び出すためにsuper()を使用しないことを願っています...
Ned Batchelder '24

7

Pythonの属性ルックアップに関する非常に興味深い点の1つは、「仮想変数」の作成に使用できることです。

class A(object):

  label="Amazing"

  def __init__(self,d): 
      self.data=d

  def say(self): 
      print("%s %s!"%(self.label,self.data))

class B(A):
  label="Bold"  # overrides A.label

A(5).say()      # Amazing 5!
B(3).say()      # Bold 3!

通常、作成後にこれらへの割り当てはありません。特定のインスタンスに関連付けられていないという意味では静的ですが、値はインスタンス(のクラス)に依存しているselfため、ルックアップが使用することに注意してください。label


6

この回答に関しては、静的定数の場合、記述子を使用できます。次に例を示します。

class ConstantAttribute(object):
    '''You can initialize my value but not change it.'''
    def __init__(self, value):
        self.value = value

    def __get__(self, obj, type=None):
        return self.value

    def __set__(self, obj, val):
        pass


class Demo(object):
    x = ConstantAttribute(10)


class SubDemo(Demo):
    x = 10


demo = Demo()
subdemo = SubDemo()
# should not change
demo.x = 100
# should change
subdemo.x = 100
print "small demo", demo.x
print "small subdemo", subdemo.x
print "big demo", Demo.x
print "big subdemo", SubDemo.x

その結果 ...

small demo 10
small subdemo 100
big demo 10
big subdemo 10

設定値(pass上記)を静かに無視することが問題ではない場合は、常に例外を発生させることができます。C ++、Javaスタイルの静的クラス変数を探している場合:

class StaticAttribute(object):
    def __init__(self, value):
        self.value = value

    def __get__(self, obj, type=None):
        return self.value

    def __set__(self, obj, val):
        self.value = val

見ていこの回答や公式ドキュメントのHOWTO記述子の詳細については、を。


2
だけを使用することもできます@property。これは記述子を使用するのと同じですが、コードははるかに少なくなります。
リックはモニカサポートして

6

もちろん、Python自体には静的データメンバーは明示的にありませんが、そうすることで実現できます

class A:
    counter =0
    def callme (self):
        A.counter +=1
    def getcount (self):
        return self.counter  
>>> x=A()
>>> y=A()
>>> print(x.getcount())
>>> print(y.getcount())
>>> x.callme() 
>>> print(x.getcount())
>>> print(y.getcount())

出力

0
0
1
1

説明

here object (x) alone increment the counter variable
from 0 to 1 by not object y. But result it as "static counter"

6

はい、Pythonで静的変数とメソッドを書くことは間違いなく可能です。

静的変数: クラスレベルで宣言された変数は静的変数と呼ばれ、クラス名を使用して直接アクセスできます。

    >>> class A:
        ...my_var = "shagun"

    >>> print(A.my_var)
        shagun

インスタンス変数:クラスのインスタンスによって関連付けられ、アクセスされる変数は、インスタンス変数です。

   >>> a = A()
   >>> a.my_var = "pruthi"
   >>> print(A.my_var,a.my_var)
       shagun pruthi

静的メソッド:変数と同様に、静的メソッドはクラス名を使用して直接アクセスできます。インスタンスを作成する必要はありません。

ただし、Pythonでは静的メソッドが非静的メソッドを呼び出すことはできません。

    >>> class A:
   ...     @staticmethod
   ...     def my_static_method():
   ...             print("Yippey!!")
   ... 
   >>> A.my_static_method()
   Yippey!!

4

潜在的な混乱を避けるために、静的変数と不変オブジェクトを対比したいと思います。

整数、浮動小数点数、文字列、touplesのようないくつかのプリミティブオブジェクトタイプは、Pythonでは不変です。つまり、特定の名前で参照されるオブジェクトは、前述のオブジェクトタイプのいずれかである場合は変更できません。名前は別のオブジェクトに再割り当てできますが、オブジェクト自体は変更できません。

変数を静的にすることで、変数名が現在指しているオブジェクト以外のオブジェクトを指すことを禁止することで、これをさらに一歩進めます。(注:これは一般的なソフトウェアの概念であり、Pythonに固有のものではありません。Pythonで静的を実装する方法については、他の投稿を参照してください)。


4

私が見つけた最良の方法は、別のクラスを使用することです。オブジェクトを作成して、それを他のオブジェクトで使用できます。

class staticFlag:
    def __init__(self):
        self.__success = False
    def isSuccess(self):
        return self.__success
    def succeed(self):
        self.__success = True

class tryIt:
    def __init__(self, staticFlag):
        self.isSuccess = staticFlag.isSuccess
        self.succeed = staticFlag.succeed

tryArr = []
flag = staticFlag()
for i in range(10):
    tryArr.append(tryIt(flag))
    if i == 5:
        tryArr[i].succeed()
    print tryArr[i].isSuccess()

上記の例では、というクラスを作成しましたstaticFlag

このクラスは静的変数__success(Private Static Var)を提示する必要があります。

tryIt クラスは、使用する必要がある通常のクラスを表します。

次に、1つのフラグのオブジェクトを作成しました(staticFlag)。このフラグは、すべての通常のオブジェクトへの参照として送信されます。

これらのオブジェクトはすべてリストに追加されていますtryArr


このスクリプトの結果:

False
False
False
False
False
True
True
True
True
True

2

クラスファクトリpython3.6の静的変数

python3.6以降でクラスファクトリを使用している場合は、nonlocalキーワードを使用して、作成中のクラスのスコープ/コンテキストに次のように追加します。

>>> def SomeFactory(some_var=None):
...     class SomeClass(object):
...         nonlocal some_var
...         def print():
...             print(some_var)
...     return SomeClass
... 
>>> SomeFactory(some_var="hello world").print()
hello world

はい、しかしこの場合hasattr(SomeClass, 'x')Falseです。私はこれが静的変数によって何を意味するのかを疑っています。
リックは2017

@RickTeachey lol、静的変数コード、stackoverflow.com / a / 27568860/2026508 +1インターネットサーバーを見て、hasattrがそのように動作しないと思いましたか?そうでsome_var不変、および静的に定義された、またはそうではありませんか?外部ゲッターアクセスは、変数が静的であるかどうかに関係していますか?今、たくさん質問があります。時間があるときは、いくつかの答えを聞きたいです。
jmunsch 2017

ええ、そのメタクラスはかなりばかげています。質問が理解できるかどうかはわかりませんが、私の頭でsome_varは、上記はクラスのメンバーではありません。Pythonでは、すべてのクラスメンバーにクラスの外部からアクセスできます。
リックはモニカをサポートする

nonlocalkeywoardは、変数のスコープを「バンプ」。クラス本体定義のスコープは、それ自体が見つけたスコープとは無関係です。つまりnonlocal some_var、別の名前付きオブジェクトへの非ローカル(クラス定義スコープではない)の名前参照を作成するだけです。したがって、クラス本体のスコープにないため、クラス定義にアタッチされません。
リックはモニカをサポートします

1

これはおそらくハックですが、私は eval(str) python 3で矛盾のような静的オブジェクトを取得するためにしています。

class一部の引数を保存する静的メソッドとコンストラクターで定義されたオブジェクトのみを含むRecords.pyファイルがあります。次に、別の.pyファイルからimport Records、各オブジェクトを動的に選択し、読み込まれるデータのタイプに応じてオンデマンドでインスタンス化する必要があります。

したがってobject_name = 'RecordOne'、クラス名のどこで、私はcur_type = eval(object_name)それを呼び出してインスタンス化しますが、インスタンス化するcur_inst = cur_type(args) 前にcur_type.getName()、たとえば抽象的な基本クラスの実装などの目的から静的メソッドを呼び出すことができます。ただし、バックエンドでは、それはおそらくPythonでインスタンス化され、真に静的ではありません。なぜなら、evalはオブジェクトを返しているからです。


0

リストまたは辞書を使用して、インスタンス間の「静的な動作」を取得できます。

class Fud:

     class_vars = {'origin_open':False}

     def __init__(self, origin = True):
         self.origin = origin
         self.opened = True
         if origin:
             self.class_vars['origin_open'] = True


     def make_another_fud(self):
         ''' Generating another Fud() from the origin instance '''

         return Fud(False)


     def close(self):
         self.opened = False
         if self.origin:
             self.class_vars['origin_open'] = False


fud1 = Fud()
fud2 = fud1.make_another_fud()

print (f"is this the original fud: {fud2.origin}")
print (f"is the original fud open: {fud2.class_vars['origin_open']}")
# is this the original fud: False
# is the original fud open: True

fud1.close()

print (f"is the original fud open: {fud2.class_vars['origin_open']}")
# is the original fud open: False

0

たとえば、他のインスタンス間でそれを増やすために静的変数を共有しようとしている場合、次のスクリプトのようなものがうまく機能します。

# -*- coding: utf-8 -*-
class Worker:
    id = 1

    def __init__(self):
        self.name = ''
        self.document = ''
        self.id = Worker.id
        Worker.id += 1

    def __str__(self):
        return u"{}.- {} {}".format(self.id, self.name, self.document).encode('utf8')


class Workers:
    def __init__(self):
        self.list = []

    def add(self, name, doc):
        worker = Worker()
        worker.name = name
        worker.document = doc
        self.list.append(worker)


if __name__ == "__main__":
    workers = Workers()
    for item in (('Fiona', '0009898'), ('Maria', '66328191'), ("Sandra", '2342184'), ('Elvira', '425872')):
        workers.add(item[0], item[1])
    for worker in workers.list:
        print(worker)
    print("next id: %i" % Worker.id)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.