Các biến lớp tĩnh có thể có trong Python không?


Câu trả lời:


1900

Các biến được khai báo bên trong định nghĩa lớp, nhưng không phải bên trong một phương thức là các biến lớp hoặc biến tĩnh:

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

Như @ millerdev chỉ ra, điều này tạo ra một ibiến cấp độ lớp, nhưng điều này khác với bất kỳ ibiến cấp độ cá thể nào , vì vậy bạn có thể có

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

Điều này khác với C ++ và Java, nhưng không khác với C #, nơi một thành viên tĩnh không thể được truy cập bằng cách sử dụng một tham chiếu đến một thể hiện.

Xem những gì hướng dẫn Python nói về chủ đề của các lớp và các đối tượng lớp .

@Steve Johnson đã trả lời về các phương thức tĩnh , cũng được ghi lại trong phần "Các hàm tích hợp" trong Tài liệu tham khảo thư viện Python .

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

@beidy đề xuất classmethod s so với staticmethod, vì phương thức sau đó nhận loại lớp làm đối số đầu tiên, nhưng tôi vẫn hơi mờ về những lợi thế của phương pháp này so với phương pháp tĩnh. Nếu bạn cũng vậy, thì có lẽ không vấn đề gì.


11
Tôi chỉ học Python, nhưng những ưu điểm của @classmethodhơn @staticmethodAFAIK là bạn luôn luôn nhận được tên của lớp phương pháp này đã được gọi vào, ngay cả khi đó là một lớp con. Một phương thức tĩnh thiếu thông tin này, vì vậy nó không thể gọi một phương thức bị ghi đè.
Seb

49
@theJollySin cách pythonic cho các hằng số là không phát triển một lớp cho các hằng số. Chỉ cần có một số const.pyvới PI = 3.14và bạn có thể nhập nó ở khắp mọi nơi. from const import PI
Giszmo

30
Câu trả lời này có khả năng gây nhầm lẫn vấn đề biến tĩnh. Để bắt đầu, i = 3không một biến tĩnh, nó là một thuộc tính lớp, và vì nó là riêng biệt từ một thuộc tính dụ cấp ikhông hành xử giống như một biến tĩnh trong các ngôn ngữ khác. Xem câu trả lời millerdev của , câu trả lời của Yann , và câu trả lời của tôi dưới đây.
Rick hỗ trợ Monica

2
vì vậy chỉ có một bản sao của i(biến tĩnh) sẽ có trong bộ nhớ ngay cả khi tôi tạo hàng trăm thể hiện của lớp này?
sdream

2
Đối với bất kỳ ai quan tâm, người mà Daniel đã đề cập trong bình luận @Dublow, đó là millerdev ( máy quay ngược )
HeyJude

618

@Blair Conrad cho biết các biến tĩnh được khai báo bên trong định nghĩa lớp, nhưng không phải bên trong một phương thức là các biến lớp hoặc "tĩnh":

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

Có một vài gotcha ở đây. Tiếp tục từ ví dụ trên:

>>> 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__
{}

Lưu ý cách biến t.iđối tượng không đồng bộ với biến lớp "tĩnh" khi thuộc tính iđược đặt trực tiếp t. Điều này là do iđược liên kết lại trong tkhông gian tên, khác với Testkhông gian tên. Nếu bạn muốn thay đổi giá trị của biến "tĩnh", bạn phải thay đổi nó trong phạm vi (hoặc đối tượng) nơi nó được xác định ban đầu. Tôi đặt "static" trong dấu ngoặc kép vì Python không thực sự có biến tĩnh theo nghĩa mà C ++ và Java làm.

Mặc dù nó không nói bất cứ điều gì cụ thể về các biến hoặc phương thức tĩnh, hướng dẫn Python có một số thông tin liên quan về các lớp và các đối tượng lớp .

@Steve Johnson cũng đã trả lời về các phương thức tĩnh, cũng được ghi lại trong phần "Các hàm tích hợp" trong Tài liệu tham khảo thư viện Python.

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

@beid cũng đề cập đến classmethod, tương tự như staticmethod. Đối số đầu tiên của classmethod là đối tượng lớp. Thí dụ:

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

Đại diện hình ảnh của ví dụ trên


3
Tôi đề nghị bạn mở rộng ví dụ chỉ một chút: nếu, sau khi đặt Test.i = 6, sau đó bạn khởi tạo một đối tượng mới (ví dụ: u = Test ()), đối tượng mới sẽ "kế thừa" giá trị lớp mới (ví dụ: ui == 6)
Đánh dấu

2
Một cách để giữ các biến tĩnh trong đồng bộ hóa là làm cho họ đặc tính: class Test(object):, _i = 3, @property, def i(self), return type(self)._i, @i.setter, def i(self,val):, type(self)._i = val. Bây giờ bạn có thể làm x = Test(), x.i = 12, assert x.i == Test.i.
Rick hỗ trợ Monica

1
Vì vậy, tôi có thể nói tất cả các biến là tĩnh ban đầu và sau đó truy cập các thể hiện tạo các biến thể hiện trong thời gian chạy?
Ali

Có lẽ điều này rất thú vị: nếu bạn xác định một phương thức trong Test thay đổi Test.i, điều đó sẽ ảnh hưởng đến giá trị BOTH Test.i và ti.
Pablo

@millerdev, như bạn đã đề cập Python không có biến tĩnh như C ++ hay JAVA có..Vậy thì có ổn không khi nói, Test.i là một biến lớp hơn là biến tĩnh?
Tyto

197

Phương thức tĩnh và lớp

Như các câu trả lời khác đã lưu ý, các phương thức tĩnh và lớp dễ dàng được thực hiện bằng cách sử dụng các trình trang trí tích hợp:

class Test(object):

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

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

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

Như thường lệ, đối số đầu tiên MyMethod()được liên kết với đối tượng thể hiện của lớp. Ngược lại, đối số đầu tiên MyClassMethod()được ràng buộc với chính đối tượng lớp (ví dụ, trong trường hợp này, Test). Đối với MyStaticMethod(), không có đối số nào bị ràng buộc và có tất cả các đối số là tùy chọn.

"Biến tĩnh"

Tuy nhiên, việc thực hiện "các biến tĩnh" (tốt, các biến tĩnh có thể thay đổi , dù sao đi nữa, nếu đó không phải là một mâu thuẫn trong điều khoản ...) thì không đơn giản như vậy. Như millerdev đã chỉ ra trong câu trả lời của mình , vấn đề là các thuộc tính lớp của Python không thực sự là "biến tĩnh". Xem xét:

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

Điều này là do các dòng x.i = 12đã được thêm vào một thuộc tính thể hiện mới iđể xthay vì thay đổi giá trị của các Testlớp ithuộc tính.

Hành vi biến tĩnh dự kiến một phần , nghĩa là đồng bộ hóa thuộc tính giữa nhiều trường hợp (nhưng không phải với chính lớp đó, xem "gotcha" bên dưới), có thể đạt được bằng cách biến thuộc tính lớp thành thuộc tính:

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)

Bây giờ bạn có thể làm:

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

Biến tĩnh bây giờ sẽ vẫn đồng bộ giữa tất cả các thể hiện của lớp .

(LƯU Ý: Đó là, trừ khi một thể hiện của lớp quyết định xác định phiên bản của chính nó _i! Nhưng nếu ai đó quyết định làm THAT, họ xứng đáng với những gì họ nhận được, phải không ???)

Lưu ý rằng về mặt kỹ thuật, ivẫn không phải là một "biến tĩnh"; nó là một propertyloại mô tả đặc biệt Tuy nhiên, propertyhành vi hiện tương đương với một biến tĩnh (có thể thay đổi) được đồng bộ hóa trên tất cả các thể hiện của lớp.

"Biến tĩnh" bất biến

Đối với hành vi biến tĩnh bất biến, chỉ cần bỏ qua 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)

Bây giờ cố gắng đặt ithuộc tính thể hiện sẽ trả về AttributeError:

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

Một Gotcha phải cảnh giác

Lưu ý rằng các phương thức trên chỉ hoạt động với các thể hiện của lớp của bạn - chúng sẽ không hoạt động khi sử dụng chính lớp đó . Ví dụ:

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'

Dòng này assert Test.i == x.itạo ra một lỗi, bởi vì ithuộc tính của Testxlà hai đối tượng khác nhau.

Nhiều người sẽ thấy điều này đáng ngạc nhiên. Tuy nhiên, nó không nên Nếu chúng tôi quay lại và kiểm tra Testđịnh nghĩa lớp của chúng tôi (phiên bản thứ hai), chúng tôi lưu ý đến dòng này:

    i = property(get_i) 

Rõ ràng, thành viên icủa Testphải là một propertyđối tượng, là loại đối tượng được trả về từ propertyhàm.

Nếu bạn thấy khó hiểu ở trên, rất có thể bạn vẫn đang nghĩ về nó từ quan điểm của các ngôn ngữ khác (ví dụ Java hoặc c ++). Bạn nên đi nghiên cứu propertyđối tượng, về thứ tự các thuộc tính Python được trả về, giao thức mô tả và thứ tự phân giải phương thức (MRO).

Tôi trình bày một giải pháp cho 'gotcha' ở trên; tuy nhiên tôi sẽ đề nghị - vất vả - rằng bạn không cố gắng làm điều gì đó như sau cho đến khi - tối thiểu - bạn hiểu thấu đáo lý do tại sao assert Test.i = x.igây ra lỗi.

REAL, THỰC TẾ Biến tĩnh -Test.i == x.i

Tôi trình bày giải pháp (Python 3) dưới đây chỉ cho mục đích thông tin. Tôi không xác nhận nó là một "giải pháp tốt". Tôi nghi ngờ liệu việc mô phỏng hành vi biến tĩnh của các ngôn ngữ khác trong Python có thực sự cần thiết hay không. Tuy nhiên, bất kể nó có thực sự hữu ích hay không, phần dưới đây sẽ giúp hiểu thêm về cách thức hoạt động của Python.

CẬP NHẬT: nỗ lực này thực sự khá khủng khiếp ; nếu bạn khăng khăng làm điều gì đó như thế này (gợi ý: xin đừng; Python là một ngôn ngữ rất tao nhã và khiến nó hành xử như một ngôn ngữ khác là không cần thiết), thay vào đó , hãy sử dụng mã trong câu trả lời của Ethan Furman .

Mô phỏng hành vi biến tĩnh của các ngôn ngữ khác bằng cách sử dụng siêu dữ liệu

Một metaclass là lớp của một lớp. Siêu dữ liệu mặc định cho tất cả các lớp trong Python (nghĩa là các lớp "kiểu mới" đăng Python 2.3 tôi tin) là type. Ví dụ:

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

Tuy nhiên, bạn có thể định nghĩa siêu dữ liệu của riêng mình như thế này:

class MyMeta(type): pass

Và áp dụng nó cho lớp của riêng bạn như thế này (chỉ Python 3):

class MyClass(metaclass = MyMeta):
    pass

type(MyClass)  # class MyMeta

Dưới đây là một siêu dữ liệu tôi đã tạo để cố gắng mô phỏng hành vi "biến tĩnh" của các ngôn ngữ khác. Về cơ bản, nó hoạt động bằng cách thay thế getter, setter và deleter mặc định bằng các phiên bản kiểm tra xem liệu thuộc tính đang được yêu cầu có phải là "biến tĩnh" hay không.

Một danh mục của "các biến tĩnh" được lưu trữ trong StaticVarMeta.staticsthuộc tính. Tất cả các yêu cầu thuộc tính ban đầu được cố gắng giải quyết bằng cách sử dụng lệnh giải quyết thay thế. Tôi đã đặt tên này là "thứ tự độ phân giải tĩnh" hay "SRO". Điều này được thực hiện bằng cách tìm kiếm thuộc tính được yêu cầu trong tập hợp "biến tĩnh" cho một lớp nhất định (hoặc các lớp cha của nó). Nếu thuộc tính không xuất hiện trong "SRO", lớp sẽ rơi trở lại thuộc tính get / set / xóa thuộc tính mặc định (nghĩa là "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

Tôi đã cố gắng sử dụng theo cách của bạn nhưng tôi gặp phải một vấn đề, vui lòng xem câu hỏi của tôi ở đây stackoverflow.com/questions/29329850/get-static-variable-value
Muhammed Refaat

@RickTeachey: Tôi đoán bạn thường nên xem bất cứ điều gì bạn làm trên Instance của lớp Test(trước khi sử dụng nó để khởi tạo các thể hiện) như là trong miền lập trình meta? Chẳng hạn, bạn thay đổi hành vi lớp bằng cách thực hiện Test.i = 0(ở đây bạn chỉ cần hủy hoàn toàn đối tượng thuộc tính). Tôi đoán "cơ chế thuộc tính" chỉ kích hoạt quyền truy cập thuộc tính trong các trường hợp của một lớp (trừ khi bạn thay đổi hành vi cơ bản bằng cách sử dụng lớp meta làm trung gian, có lẽ). Btw, vui lòng hoàn thành câu trả lời này :-)
Ole Thomsen Buus

1
@RickTeachey Cảm ơn :-) Siêu dữ liệu của bạn cuối cùng rất thú vị nhưng thực sự hơi phức tạp đối với sở thích của tôi. Nó có thể hữu ích trong một khung / ứng dụng lớn trong đó cơ chế này là hoàn toàn bắt buộc. Dù sao, điều này minh họa rằng nếu hành vi meta không mặc định (phức tạp) mới thực sự cần thiết, Python làm cho nó có thể :)
Ole Thomsen Buus

1
@OleThomsenBuus: Kiểm tra câu trả lời của tôi cho một siêu dữ liệu đơn giản hơn thực hiện công việc.
Ethan Furman

1
@taper Bạn đúng rồi; Tôi đã chỉnh sửa câu trả lời để khắc phục sự cố (không thể tin rằng nó đã ngồi sai quá lâu!). Xin lỗi vì sự nhầm lẫn.
Rick hỗ trợ Monica

33

Bạn cũng có thể thêm các biến lớp vào các lớp một cách nhanh chóng

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

Và các thể hiện của lớp có thể thay đổi các biến lớp

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

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

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

3
Các biến lớp mới sẽ dính ngay cả khi lớp được nhập vào một mô-đun khác?
zakdances

Đúng. Các lớp là singletons hiệu quả, bất kể không gian tên bạn gọi chúng từ.
Pedro

@Gregory bạn đã nói "Và các thể hiện của lớp có thể thay đổi các biến lớp" Trên thực tế ví dụ này được gọi là truy cập không sửa đổi. Việc sửa đổi được thực hiện bởi chính đối tượng thông qua hàm append () của chính nó.
Amr ALHOSSary

19

Cá nhân tôi sẽ sử dụng một classmethod bất cứ khi nào tôi cần một phương thức tĩnh. Chủ yếu là vì tôi lấy lớp làm đối số.

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

hoặc sử dụng một trang trí

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

Đối với các thuộc tính tĩnh .. Đã đến lúc bạn tra cứu một số định nghĩa python .. biến luôn có thể thay đổi. Có hai loại trong số chúng có thể thay đổi và không thay đổi .. Ngoài ra, có các thuộc tính lớp và thuộc tính thể hiện .. Không có gì thực sự giống như các thuộc tính tĩnh theo nghĩa của java & c ++

Tại sao sử dụng phương thức tĩnh theo nghĩa pythonic, nếu nó không liên quan gì đến lớp! Nếu tôi là bạn, tôi sẽ sử dụng classmethod hoặc định nghĩa phương thức độc lập với lớp.


1
Các biến không biến đổi hoặc bất biến; đối tượng là. (Tuy nhiên, một đối tượng có thể, với các mức độ thành công khác nhau, cố gắng ngăn việc gán cho một số thuộc tính của nó.)
Davis Herring

Java và C ++ sử dụng tĩnh (không sử dụng từ, imho) chính xác như bạn sử dụng thể hiện so với thuộc tính lớp. Một thuộc tính / phương thức lớp là tĩnh trong Java và C ++, không có sự khác biệt, ngoại trừ trong Python, tham số đầu tiên cho một cuộc gọi phương thức lớp là lớp.
Thiên thần O'Sphere

16

Một điều đặc biệt cần lưu ý về thuộc tính tĩnh & thuộc tính thể hiện, được hiển thị trong ví dụ bên dưới:

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

Điều này có nghĩa là trước khi gán giá trị cho thuộc tính cá thể, nếu chúng ta cố gắng truy cập vào thể hiện của thuộc tính, giá trị tĩnh được sử dụng. Mỗi thuộc tính được khai báo trong lớp python luôn có một khe tĩnh trong bộ nhớ .


16

Các phương thức tĩnh trong python được gọi là classmethod s. Hãy xem đoạn mã sau

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

Lưu ý rằng khi chúng ta gọi phương thức myInstanceMethod , chúng ta sẽ gặp lỗi. Điều này là do nó yêu cầu phương thức đó được gọi trong một thể hiện của lớp này. Phương pháp myStaticMethod được thiết lập như một classmethod sử dụng trang trí @classmethod .

Chỉ cần đá và cười khúc khích, chúng ta có thể gọi myInstanceMethod trên lớp bằng cách chuyển qua một thể hiện của lớp, như vậy:

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

2
Umm ... phương thức tĩnh được thực hiện với @staticmethod; @classmethodlà (rõ ràng) cho các phương thức lớp (chủ yếu được sử dụng như là các hàm tạo thay thế, nhưng có thể phục vụ trong một nhúm như các phương thức tĩnh xảy ra để nhận một tham chiếu đến lớp mà chúng được gọi qua).
ShadowRanger

11

Khi định nghĩa một số biến thành viên bên ngoài bất kỳ phương thức thành viên nào, biến có thể là tĩnh hoặc không tĩnh tùy thuộc vào cách biến được thể hiện.

  • ClassNAME.var là biến tĩnh
  • INSTANCENAME.var không phải là biến tĩnh.
  • self.var bên trong lớp không phải là biến tĩnh.
  • var bên trong hàm thành viên lớp không được xác định.

Ví dụ:

#!/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()

Kết quả là

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

Các vết lõm bị hỏng. Điều này sẽ không được thực thi
Thomas Weller

9

Có thể có staticcác biến lớp, nhưng có lẽ không đáng để nỗ lực.

Đây là một bằng chứng về khái niệm được viết bằng Python 3 - nếu bất kỳ chi tiết chính xác nào sai, mã có thể được điều chỉnh để phù hợp với bất cứ điều gì bạn muốn nói 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)

và đang sử dụng:

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

và một số xét nghiệm:

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

Bạn cũng có thể thực thi một lớp tĩnh bằng siêu dữ liệu.

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

Sau đó, bất cứ khi nào bạn vô tình cố gắng khởi tạo MyClass, bạn sẽ nhận được một StaticClassError.


4
Tại sao nó thậm chí là một lớp nếu bạn sẽ không khởi tạo nó? Cảm giác này giống như vặn Python để biến nó thành Java ....
Ned Batchelder

1
Thành ngữ Borg là một cách tốt hơn để xử lý việc này.
Rick hỗ trợ Monica

@NedBatchelder Đó là một lớp trừu tượng, chỉ dành cho phân lớp (và khởi tạo các lớp con)
stevepastelan

1
Tôi hy vọng các lớp con không sử dụng super () để gọi __new__cha mẹ của nó ...
Ned Batchelder

7

Một điểm rất thú vị về tra cứu thuộc tính của Python là nó có thể được sử dụng để tạo " biến ảo ":

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!

Thông thường không có bất kỳ sự phân công nào sau khi chúng được tạo. Lưu ý rằng việc tra cứu sử dụng selfbởi vì, mặc dù labellà tĩnh theo nghĩa không được liên kết với một thể hiện cụ thể, giá trị vẫn phụ thuộc vào thể hiện (lớp của).


6

Liên quan đến câu trả lời này , đối với một biến tĩnh không đổi, bạn có thể sử dụng một mô tả. Đây là một ví dụ:

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

dẫn đến ...

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

Bạn luôn có thể đưa ra một ngoại lệ nếu lặng lẽ bỏ qua giá trị cài đặt ( passở trên) không phải là điều của bạn. Nếu bạn đang tìm kiếm một biến lớp tĩnh C ++, kiểu 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

Hãy xem câu trả lời này và các tài liệu chính thức HOWTO để biết thêm thông tin về các mô tả.


2
Bạn cũng có thể chỉ sử dụng @property, tương tự như sử dụng một bộ mô tả, nhưng nó ít mã hơn rất nhiều.
Rick hỗ trợ Monica

6

Hoàn toàn có, Python tự nó không có bất kỳ thành viên dữ liệu tĩnh nào, nhưng chúng ta có thể có bằng cách làm như vậy

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

đầu ra

0
0
1
1

giải trình

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

6

Có, chắc chắn có thể viết các biến và phương thức tĩnh trong python.

Biến tĩnh: Biến được khai báo ở cấp lớp được gọi là biến tĩnh có thể được truy cập trực tiếp bằng tên lớp.

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

    >>> print(A.my_var)
        shagun

Biến sơ thẩm: Các biến có liên quan và được truy cập bởi thể hiện của một lớp là các biến thể hiện.

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

Phương pháp tĩnh: Tương tự như các biến, các phương thức tĩnh có thể được truy cập trực tiếp bằng Tên lớp. Không cần phải tạo một ví dụ.

Nhưng hãy nhớ, một phương thức tĩnh không thể gọi một phương thức không tĩnh trong python.

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

4

Để tránh bất kỳ sự nhầm lẫn tiềm ẩn nào, tôi muốn đối chiếu các biến tĩnh và các đối tượng bất biến.

Một số loại đối tượng nguyên thủy như số nguyên, số float, chuỗi và touples là bất biến trong Python. Điều này có nghĩa là đối tượng được gọi bằng một tên cụ thể không thể thay đổi nếu nó thuộc một trong các loại đối tượng đã nói ở trên. Tên có thể được gán lại cho một đối tượng khác, nhưng bản thân đối tượng có thể không được thay đổi.

Làm cho một biến tĩnh thực hiện bước này thêm một bước nữa bằng cách không cho phép tên biến để trỏ đến bất kỳ đối tượng nào mà nó hiện đang trỏ tới. (Lưu ý: đây là khái niệm phần mềm chung và không dành riêng cho Python; vui lòng xem bài đăng của người khác để biết thông tin về việc triển khai thống kê trong Python).


4

Cách tốt nhất tôi tìm thấy là sử dụng một lớp khác. Bạn có thể tạo một đối tượng và sau đó sử dụng nó trên các đối tượng khác.

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

Với ví dụ trên, tôi đã tạo một lớp có tên staticFlag.

Lớp này sẽ trình bày var tĩnh __success(Private static Var).

tryIt lớp đại diện cho lớp thông thường chúng ta cần sử dụng.

Bây giờ tôi đã tạo một đối tượng cho một cờ ( staticFlag). Cờ này sẽ được gửi làm tham chiếu cho tất cả các đối tượng thông thường.

Tất cả những đối tượng này đang được thêm vào danh sách tryArr.


Kết quả Script này:

False
False
False
False
False
True
True
True
True
True

2

Biến tĩnh trong lớp nhà máy python3.6

Đối với bất kỳ ai sử dụng một nhà máy lớp với python3.6 trở lên, hãy sử dụng nonlocaltừ khóa để thêm nó vào phạm vi / bối cảnh của lớp được tạo như vậy:

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

vâng, nhưng trong trường hợp này hasattr(SomeClass, 'x')False. Tôi nghi ngờ đây là những gì bất cứ ai có nghĩa là bởi một biến tĩnh.
Rick hỗ trợ Monica

@RickTeachey lol, đã thấy mã biến tĩnh của bạn, stackoverflow.com/a/27568860/2026508 +1 internet thưa ông, và tôi nghĩ hasattr không hoạt động như vậy? vậy là some_varbất biến, và được định nghĩa tĩnh, hay không? Điều gì bên ngoài truy cập getter phải làm gì với một biến là tĩnh hay không? bây giờ tôi có rất nhiều câu hỏi rất thích nghe một số câu trả lời khi bạn có thời gian.
jmunsch

Vâng, metaclass là khá vô lý. Tôi không chắc chắn tôi hiểu các câu hỏi nhưng với suy nghĩ của tôi, some_varở trên không phải là một thành viên trong lớp. Trong Python tất cả các thành viên lớp có thể được truy cập từ bên ngoài lớp.
Rick hỗ trợ Monica

Các nonlocalkeywoard "da gà" phạm vi của biến. Phạm vi của một định nghĩa thân lớp là độc lập với phạm vi mà nó tự tìm thấy - khi bạn nói nonlocal some_var, đó chỉ là tạo một tham chiếu tên không phải cục bộ (đọc: KHÔNG trong phạm vi định nghĩa lớp) cho một đối tượng có tên khác. Do đó, nó không được gắn với định nghĩa lớp vì nó không nằm trong phạm vi thân lớp.
Rick ủng hộ Monica

1

Vì vậy, đây có thể là một hack, nhưng tôi đã sử dụng eval(str)để có được một đối tượng tĩnh, loại mâu thuẫn, trong python 3.

Có một tệp Records.txt không có gì ngoài classcác đối tượng được xác định bằng các phương thức và hàm tạo tĩnh lưu một số đối số. Sau đó, từ một tệp .py khác import Recordsnhưng tôi cần phải tự động chọn từng đối tượng và sau đó khởi tạo nó theo yêu cầu theo loại dữ liệu được đọc.

Vì vậy, ở đâu object_name = 'RecordOne'hoặc tên lớp, tôi gọi cur_type = eval(object_name)và sau đó để khởi tạo nó bạn làm cur_inst = cur_type(args) Tuy nhiên trước khi bạn khởi tạo, bạn có thể gọi các phương thức tĩnh từ cur_type.getName()ví dụ, giống như thực hiện lớp cơ sở trừu tượng hoặc bất cứ mục tiêu nào. Tuy nhiên, trong phần phụ trợ, nó có thể được khởi tạo bằng python và không thực sự tĩnh, bởi vì eval đang trả về một đối tượng .... mà phải được khởi tạo .... nó mang lại hành vi giống như tĩnh.


0

Bạn có thể sử dụng danh sách hoặc từ điển để nhận "hành vi tĩnh" giữa các trường hợp.

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

Nếu bạn đang cố gắng chia sẻ một biến tĩnh, ví dụ, tăng nó trên các trường hợp khác, một cái gì đó như tập lệnh này hoạt động tốt:

# -*- 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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.