Pythonで静的クラス変数またはメソッドを持つことは可能ですか? これを行うにはどのような構文が必要ですか?
25 に答える
メソッド内ではなくクラス定義内で宣言された変数は、クラス変数または静的変数です。
>>> 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 はstaticmethod よりもclassmethodを推奨します。
@Blair Conradは、クラス定義内で宣言されているがメソッド内では宣言されていない静的変数はクラスまたは「静的」変数であると述べました。
>>> 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
が「静的」クラス変数と同期しなくなったことに注意してください。これは、名前空間とは異なる名前空間内で再バインドされたためです。「静的」変数の値を変更する場合は、最初に定義されたスコープ(またはオブジェクト)内で変更する必要があります。Pythonには、C ++やJavaのように静的変数が実際にはないため、「静的」を引用符で囲みます。i
t
i
t
Test
静的変数やメソッドについて具体的なことは何も述べていませんが、Pythonチュートリアルには、クラスとクラスオブジェクトに関するいくつかの関連情報があります。
@Steve Johnsonは、静的メソッドについても回答しました。これは、Pythonライブラリリファレンスの「組み込み関数」にも記載されています。
class Test(object):
@staticmethod
def f(arg1, arg2, ...):
...
@beidは、staticmethodに似たclassmethodについても言及しました。classmethodの最初の引数はクラスオブジェクトです。例:
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
クラス変数をその場でクラスに追加することもできます
>>> 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]
個人的には、静的メソッドが必要なときはいつでもクラスメソッドを使用します。主な理由は、クラスを引数として取得するためです。
class myObj(object):
def myMethod(cls)
...
myMethod = classmethod(myMethod)
またはデコレータを使用する
class myObj(object):
@classmethod
def myMethod(cls)
静的プロパティの場合..Python定義を検索するとき..変数は常に変更できます。それらには、可変と不変の2つのタイプがあります。また、クラス属性とインスタンス属性があります。javaとc++の意味での静的属性のようなものはありません。
クラスとは何の関係もないのに、なぜ静的メソッドをpythonicの意味で使用するのですか?私があなたなら、classmethodを使用するか、クラスから独立したメソッドを定義します。
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
以下の例に示すように、静的プロパティとインスタンス プロパティについて注意すべき点が 1 つあります。
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 クラスで宣言された各プロパティには、常にメモリ内に静的スロットがあります。
メンバーメソッドの外部でメンバー変数を定義する場合、変数の表現方法に応じて、変数は静的または非静的のいずれかになります。
- 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
クラス変数を持つことは可能static
ですが、おそらく努力する価値はありません。
Python 3 で書かれた概念実証は次のとおりです。正確な詳細のいずれかが間違っている場合は、コードを微調整して、 a で意味するものとほぼ一致させることができます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
メタクラスを使用してクラスを静的にすることもできます。
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 が発生します。
はい、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!!
潜在的な混乱を避けるために、静的変数と不変オブジェクトを対比したいと思います。
整数、浮動小数点数、文字列、トゥープルなどの一部のプリミティブオブジェクトタイプは、Pythonでは不変です。つまり、特定の名前で参照されているオブジェクトは、前述のオブジェクトタイプのいずれかである場合は変更できません。名前を別のオブジェクトに再割り当てすることはできますが、オブジェクト自体を変更することはできません。
変数を静的にすることで、変数名が現在指しているオブジェクト以外のオブジェクトを指すことを禁止することで、これをさらに一歩進めます。(注:これは一般的なソフトウェアの概念であり、Pythonに固有のものではありません。Pythonでの静的な実装については、他の投稿を参照してください)。
私が見つけた最良の方法は、別のクラスを使用することです。オブジェクトを作成して、それを他のオブジェクトで使用できます。
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
class は、使用する必要がある通常のクラスを表しています。
これで、1 つのフラグ ( ) のオブジェクトを作成しましたstaticFlag
。このフラグは、すべての通常のオブジェクトへの参照として送信されます。
これらのオブジェクトはすべてリストに追加されていますtryArr
。
このスクリプトの結果:
False
False
False
False
False
True
True
True
True
True
クラスファクトリー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
たとえば、他のインスタンス間で変数を増やして静的変数を共有しようとしている場合は、次のスクリプトのようなものがうまく機能します。
# -*- 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)