파이썬에서 정적 클래스 변수가 가능합니까?


답변:


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 #과는 다릅니다.

클래스와 클래스 객체의 주제에 대해 파이썬 튜토리얼이 무엇을 말하는지 보십시오 .

@ 스티브 존슨은 이미 관한 응답 한 정적 방법 도 아래 문서화, 파이썬 라이브러리 참조에서 "내장 기능" .

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

@beidy는 권장 classmethod 방법은 다음 첫 번째 인수로 클래스 유형을 수신 할 때, StaticMethod를 통해들,하지만, 난 여전히 StaticMethod를 통해이 방법의 장점에 약간 퍼지입니다. 당신이 너무 있다면, 아마도 중요하지 않을 것입니다.


11
난 그냥 파이썬을 배우고, 그러나의 장점 @classmethod을 통해 @staticmethodAFAIK은 항상 서브 클래스의 경우에도, 메소드가 호출 된 클래스의 이름을 얻을 수 있다는 것입니다. 정적 메서드에는이 정보가 없기 때문에 예를 들어 재정의 된 메서드를 호출 할 수 없습니다.
Seb

49
@theJollySin 상수에 대한 파이썬적인 방법은 상수에 대한 클래스를 늘리지 않는 것입니다. 일부만 const.py가지고 PI = 3.14있으면 어디서나 가져올 수 있습니다. from const import PI
Giszmo 2016 년

30
이 답변은 정적 변수 문제를 혼동시킬 수 있습니다. 시작하려면 i = 3입니다 되지 는 클래스 속성이며, 인스턴스 레벨 속성 별개이기 때문에, 정적 변수 i는 않습니다 하지 다른 언어 정적 변수처럼 동작합니다. 참조 millerdev의 대답 , 얀의 대답 , 그리고 내 대답은 아래를.
Rick은 Monica

2
그래서이 i클래스의 수백 개의 인스턴스를 만들더라도 (정적 변수)의 사본 하나만 메모리에 저장됩니까?
sdream

2
Daniel이 @Dubslow 의견에서 언급 한 사람에게 관심이있는 사람은 millerdev ( wayback machine )입니다.
HeyJude

618

@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속성 i이에 직접 설정된 경우 인스턴스 변수 가 "정적"클래스 변수와 어떻게 동기화되지 않았 는지 확인하십시오 t. 네임 스페이스와는 다른 네임 스페이스 i내에서 리 바인드 되었기 때문 입니다. "정적"변수의 값을 변경하려면 원래 정의 된 범위 (또는 오브젝트) 내에서 변경해야합니다. 파이썬에는 C ++과 Java가하는 의미에서 정적 변수가 없기 때문에 "정적"을 따옴표로 묶습니다.tTest

정적 변수 또는 메소드에 대해서는 구체적으로 언급하지 않지만 Python 자습서 에는 클래스 및 클래스 객체 에 대한 관련 정보가 있습니다 .

@Steve Johnson은 또한 Python Library Reference의 "Built-in Functions"에 문서화 된 정적 메소드에 대해서도 답변했습니다.

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

@beid는 staticmethod와 유사한 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

위 예의 그림 표현


3
예제를 약간 확장하는 것이 좋습니다. Test.i = 6을 설정 한 후 새 객체 (예 : u = Test ())를 인스턴스화하면 새 객체가 새 클래스 값을 "상속"합니다 (예 : ui == 6)
Mark

2
동기화 정적 변수를 유지하는 방법은 그 속성을 확인하는 것입니다 : class Test(object):, _i = 3, @property, def i(self), return type(self)._i, @i.setter, def i(self,val):, type(self)._i = val. 이제 당신이 할 수있는 x = Test(), x.i = 12, assert x.i == Test.i.
Rick은 Monica

1
그래서 모든 변수가 처음에는 정적이라고 말할 수 있으며 인스턴스에 액세스하면 런타임에 인스턴스 변수가 만들어 집니까?
Ali

Test.i를 변경하는 Test에서 메소드를 정의하면 Test.i 및 ti 값에 영향을 미칩니다.
Pablo

@millerdev, u 언급 한 것처럼 Python에는 C ++ 또는 JAVA와 같이 정적 변수가 없습니다. 따라서 Test.i는 정적 변수가 아닌 클래스 변수에 더 가깝습니까?
Tyto

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 가 그의 답변에서 지적했듯이 , 문제는 파이썬의 클래스 속성이 실제로 "정적 변수"가 아니라는 것입니다. 치다:

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을 추가 했기 때문 입니다.xTesti

클래스 속성이 아닌 속성으로 속성을 변경하면 부분적으로 예상되는 정적 변수 동작, 즉 클래스 자체가 아닌 여러 인스턴스 간의 속성 동기화가 가능합니다 ( 아래의 "gotcha"참조).

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동작은 이제 모든 클래스 인스턴스에서 동기화 된 (변경 가능) 정적 변수와 동일합니다.

불변의 "정적 변수"

불변 정적 변수 동작의 경우 간단히 propertysetter 를 생략하십시오 .

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

인식해야 할 하나의 문제

위의 메소드 는 클래스의 인스턴스 에서만 작동 하며 클래스 자체를 사용할 때는 작동 하지 않습니다 . 예를 들어 :

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속성이 서로 다른 두 개의 객체 이므로 행 에 오류가 발생 합니다.Testx

많은 사람들이이 놀라운 것을 발견 할 것입니다. 그러나 그렇게해서는 안됩니다. 돌아가서 Test클래스 정의 (두 번째 버전)를 검사 하면이 행에 주목합니다.

    i = property(get_i) 

분명히, 부재 iTest필수가 될 property로부터 리턴 된 객체의 유형 객체 property함수.

위의 내용이 혼동 될 경우 다른 언어 (예 : Java 또는 c ++)의 관점에서 생각할 가능성이 높습니다. property파이썬 속성이 리턴되는 순서, 디스크립터 프로토콜 및 메소드 분석 순서 (MRO)에 대한 오브젝트를 연구해야합니다 .

아래의 위의 'gotcha'에 대한 해결책을 제시합니다. 그러나 나는 최소한 assert Test.i = x.i오류가 발생하는 이유를 완전히 이해할 때까지 다음과 같은 일을 시도하지 말 것을 강력하게 제안 합니다.

실제, 실제 정적 변수-Test.i == x.i

정보 제공 목적으로 만 (Python 3) 솔루션을 제시합니다. 나는 그것을 "좋은 해결책"으로 보증하지 않습니다. 파이썬에서 다른 언어의 정적 변수 동작을 에뮬레이트하는 것이 실제로 필요한지에 대한 의문이 있습니다. 그러나 실제로 유용한 지 여부에 관계없이 아래는 Python의 작동 방식을 이해하는 데 도움이됩니다.

업데이트 :이 시도 는 정말 끔찍합니다 . 이 같은 일을 주장하는 경우 (힌트 : 제발하지 마십시오; 파이썬은 매우 우아한 언어이며 다른 언어처럼 행동하도록 구두를 휘두르는 것은 필요하지 않습니다) 대신 Ethan Furman의 답변에 코드를 사용하십시오 .

메타 클래스를 사용하여 다른 언어의 정적 변수 동작 에뮬레이션

메타 클래스는 클래스의 클래스입니다. 파이썬의 모든 클래스에 대한 기본 메타 클래스 (즉, 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

아래는 다른 언어의 "정적 변수"동작을 에뮬레이션하려는 메타 클래스입니다. 기본적으로 기본 getter, setter 및 deleter를 요청되는 속성이 "정적 변수"인지 확인하는 버전으로 대체하여 작동합니다.

"정적 변수"의 카탈로그가 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
모하메드 Refaat

@ RickTeachey : Instance 클래스에서 수행하는 모든 작업을 인스턴스 Test인스턴스화에 사용하기 전에 메타 프로그래밍 도메인에있는 것으로 일반적으로보아야한다고 생각 합니까? 예를 들어 클래스 동작을 변경하면됩니다 Test.i = 0(여기서 단순히 속성 개체를 완전히 파괴 함). 메타 속성을 중간 클래스로 사용하여 기본 동작을 변경하지 않는 한 "속성 메커니즘"은 클래스 인스턴스의 속성 액세스에서만 발생한다고 생각합니다. Btw,이 답변을 완료하십시오 :-)
Ole Thomsen Buus

1
@RickTeachey Thanks :-) 결국 메타 클래스는 흥미롭지 만 실제로는 내 취향에 비해 너무 복잡합니다. 이 메커니즘이 절대적으로 필요한 대규모 프레임 워크 / 애플리케이션에서 유용 할 수 있습니다. 어쨌든, 이것은 새로운 (복잡한) 기본이 아닌 메타 동작이 실제로 필요한 경우, 파이썬이 가능하게한다는 것을 보여줍니다 :)
Ole Thomsen Buus

1
@ OleThomsenBuus : 작업을 수행하는 더 간단한 메타 클래스에 대한 대답 을 확인하십시오 .
Ethan Furman

1
@taper 당신은 정확합니다; 문제를 해결하기 위해 답변을 편집했습니다 (오랫 동안 잘못 앉아 있다고 믿을 수 없습니다!). 혼란을 드려 죄송합니다.
Rick은 Monica

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

예. 클래스는 호출하는 네임 스페이스에 관계없이 효과적으로 싱글 톤입니다.
Pedro

@Gregory는 "클래스 인스턴스는 클래스 변수를 변경할 수 있습니다"라고 말했습니다. 실제로이 예제는 액세스 수정 아님이라고합니다. 수정은 객체 자체가 자체 append () 함수를 통해 수행했습니다.
Amr ALHOSSARY

19

개인적으로 정적 메소드가 필요할 때마다 클래스 메소드를 사용합니다. 주로 클래스를 인수로 사용하기 때문입니다.

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

또는 데코레이터를 사용하십시오

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

정적 속성의 경우. 파이썬 정의를 검색 할 때가 있습니다. 변수는 항상 변경 될 수 있습니다. 변경 가능하고 변경 불가능한 두 가지 유형이 있습니다. 또한 클래스 속성 및 인스턴스 속성이 있습니다. Java 및 C ++의 의미에서 정적 속성과 같은 것은 없습니다.

클래스와 관련이없는 경우 정적 방법을 파이썬의 의미로 사용하십시오! 내가 당신이라면 classmethod를 사용하거나 클래스와 독립적으로 메소드를 정의합니다.


1
변수는 변경 불가능하거나 변경 불가능합니다. 개체가 있습니다. (그러나, 개체는 다양한 정도의 성공으로 특정 속성에 할당되지 않도록 시도 할 수 있습니다.)
Davis Herring

Java와 C ++는 인스턴스 대 클래스 속성을 사용할 때와 같이 정적 (임의의 단어 imho 사용)을 사용합니다. 클래스 속성 / 메소드는 Java 및 C ++에서 정적이며, 파이썬에서는 클래스 메소드 호출에 대한 첫 번째 매개 변수가 클래스라는 점을 제외하면 차이가 없습니다.
Angel O'Sphere '11

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

파이썬의 정적 메소드를 클래스 메소드라고 합니다. 다음 코드를 살펴보십시오

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 2016 년

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로 바꾸는 것처럼 느껴진다 ....
Ned Batchelder

1
보그 관용구는 이 처리하는 더 좋은 방법입니다.
Rick은 Monica

그것은 단지 서브 클래스 (및 서브 클래스의 인스턴스)를위한 추상 클래스입니다 @NedBatchelder
stevepastelan

1
나는 서브 클래스가 __new__부모의 호출을 위해 super ()를 사용하지 않기를 바란다 ...
Ned Batchelder

7

파이썬의 속성 조회에 대한 한 가지 흥미로운 점은 " 가상 변수" 를 만드는 데 사용될 수 있다는 것입니다 .

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기술자를 사용하는 것과 동일하다,하지만 훨씬 덜 코드입니다.
Rick은 Monica

6

물론 그렇습니다. 파이썬 자체에는 명시 적으로 정적 데이터 멤버가 없지만 그렇게 할 수 있습니다.

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

그렇습니다. 정적 변수와 메소드를 파이썬으로 작성할 수 있습니다.

정적 변수 : 클래스 수준에서 선언 된 변수를 정적 변수라고하며 클래스 이름을 사용하여 직접 액세스 할 수 있습니다.

    >>> 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

정적 메소드 : 변수와 유사하게 정적 메소드는 클래스 이름을 사용하여 직접 액세스 할 수 있습니다. 인스턴스를 만들 필요가 없습니다.

그러나 정적 메소드는 파이썬에서 비 정적 메소드를 호출 할 수 없습니다.

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

4

잠재적 인 혼란을 피하기 위해 정적 변수와 불변의 객체를 대조하고 싶습니다.

정수, 부동 소수점, 문자열 및 첨탑과 같은 일부 기본 객체 유형은 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 class는 우리가 사용해야하는 정규 클래스를 나타냅니다.

이제 하나의 플래그 ( staticFlag)에 대한 객체를 만들었습니다 . 이 플래그는 모든 일반 객체에 대한 참조로 전송됩니다.

이 모든 개체가 목록에 추가됩니다 tryArr.


이 스크립트 결과 :

False
False
False
False
False
True
True
True
True
True

2

클래스 팩토리의 정적 변수

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입니다. 나는 이것이 정적 변수에 의해 누군가가 무엇을 의미하는지 의심합니다.
Rick은 Monica

@ RickTeachey lol, 정적 변수 코드, stackoverflow.com/a/27568860/2026508 +1 인터넷 선생님을 보았으며 hasattr이 그렇게 작동하지 않는다고 생각 했습니까? 그래서 some_var불변이고 정적으로 정의되어 있습니까? 외부 getter 액세스는 변수가 정적인지 아닌지와 관련이 있습니까? 나는 지금 너무 많은 질문이 있습니다. 당신이 시간을 얻을 때 답변을 듣고 싶어요.
jmunsch

네, 메타 클래스는 꽤 ​​말도 안됩니다. 나는 질문을 이해하고 있다고 확신하지는 않지만 내 마음에는 some_var위의 반원이 아니다. 파이썬에서는 모든 클래스 멤버가 클래스 외부에서 액세스 할 수 있습니다.
Rick은 Monica

nonlocalkeywoard 변수의 범위를 "범프". 클래스 본문 정의의 범위는 사용자가 말할 때 즉 nonlocal some_var, 다른 명명 된 객체에 대한 로컬이 아닌 (읽기 : 클래스 정의 범위에 없음) 이름 참조를 작성하는 범위와 독립적입니다 . 따라서 클래스 본문 범위에 없기 때문에 클래스 정의에 연결되지 않습니다.
Rick은 Monica

1

그래서 이것은 아마도 해킹 일 것입니다. 그러나 eval(str)파이썬 3에서 정적 객체, 일종의 모순을 얻는 데 사용 했습니다.

class정적 메소드 및 일부 인수를 저장하는 생성자로 정의 된 오브젝트 만있는 Records.py 파일이 있습니다 . 그런 다음 다른 .py 파일 import Records에서 각 객체를 동적으로 선택한 다음 읽고있는 데이터 유형에 따라 필요에 따라 인스턴스화해야합니다.

어디서 object_name = 'RecordOne'또는 클래스 이름을 호출 cur_type = eval(object_name)하고 인스턴스화하기 위해 인스턴스화 cur_inst = cur_type(args) 하기 전에 인스턴스화하기 전에 cur_type.getName()추상 기본 클래스 구현 또는 목표와 같은 정적 메소드를 호출 할 수 있습니다 . 그러나 백엔드에서는 아마도 파이썬으로 인스턴스화되고 실제로 정적이 아닙니다 .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.