Câu hỏi Biến lớp tĩnh có thể không?


Có thể có các biến lớp tĩnh hoặc các phương thức trong python? Cú pháp nào là cần thiết để thực hiện điều này?


1505
2017-09-16 01:46


gốc




Các câu trả lời:


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 cá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 cấp lớp i biến, nhưng điều này khác với bất kỳ cấp độ cá thể nào i biến, 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 #, trong đó 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 cá thể.

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

@Steve Johnson đã trả lời về phương pháp tĩnh, cũng được ghi lại trong "Hàm dựng sẵn" trong Tham chiếu thư viện Python.

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

@beidy đề xuất classmethods trên staticmethod, như là phương pháp sau đó nhận được loại lớp như là đối số đầu tiên, nhưng tôi vẫn còn một chút mờ về những lợi thế của phương pháp này trên staticmethod. Nếu bạn cũng vậy, thì có lẽ nó không quan trọng.


1479
2017-09-16 01:51



Tôi chỉ học Python, nhưng ưu điểm của @classmethod kết thúc @staticmethod AFAIK là bạn luôn nhận được tên của lớp mà phương thức được gọi, ngay cả khi nó 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 ghi đè, ví dụ. - Seb
@theJollySin cách pythonic cho hằng số là không phát triển một lớp cho hằng số. Chỉ cần có một số const.py với PI = 3.14 và bạn có thể nhập nó ở mọi nơi. from const import PI - Giszmo
Câu trả lời này có khả năng gây nhầm lẫn cho vấn đề biến tĩnh. Để bắt đầu với, i = 3 Là không phải một biến tĩnh, nó là một thuộc tính lớp và vì nó khác biệt với một thuộc tính mức cá thể i nó có không phải hoạt động như một biến tĩnh trong các ngôn ngữ khác. Xem câu trả lời của millerdev, Câu trả lời của Yannvà câu trả lời của tôi phía dưới. - Rick Teachey
do đó chỉ có một bản sao i(biến tĩnh) sẽ có trong bộ nhớ ngay cả khi tôi tạo ra hàng trăm cá thể của lớp này? - sdream
Đối với bất cứ ai quan tâm đến những người Daniel đã đề cập trong bình luận @Dubslow, nó là millerdev (máy rút tiền) - HeyJude


@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 hình ảnh xác thực ở đây. Thực hiện 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 cá thể t.i không đồng bộ với biến lớp "tĩnh" khi thuộc tính i được đặt trực tiếp trên t. Điều này là bởi vì i đã bị ràng buộc lại trong t không gian tên, khác biệt với Test khô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 giá trị đó trong phạm vi (hoặc đối tượng) mà ban đầu nó được xác định. Tôi đặt "tĩnh" trong dấu ngoặc kép vì Python không thực sự có các biến tĩnh theo nghĩa C ++ và Java.

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

@Steve Johnson cũng đã trả lời về các phương pháp tĩnh, cũng được ghi lại trong "Hàm dựng sẵn" trong Tham chiếu 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 the the same as  Test.i = arg1

Pictorial Representation Of Above Example


525
2017-09-16 03:04



Tôi đề nghị bạn mở rộng ví dụ chỉ một chút: nếu, sau khi thiết lập Test.i = 6, bạn sau đó 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) - Mark
Một cách để giữ cho các biến tĩnh đồng bộ hóa là làm cho chúng trở 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. Bây giờ bạn có thể làm x = Test(), x.i = 12, assert x.i == Test.i. - Rick Teachey
Vì vậy, tôi có thể nói rằng tất cả các biến là tĩnh ban đầu và sau đó truy cập các trường hợp làm cho các biến mẫu tại thời gian chạy? - Ali
Có lẽ điều này là thú vị: nếu bạn định nghĩa một phương thức trong Test để thay đổi Test.i, điều đó sẽ ảnh hưởng đến BOTH Test.i và t.i values. - Pablo


Phương pháp 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 được thực hiện dễ dàng bằng cách sử dụng các trình trang trí dựng sẵn:

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 gắn với đối tượng thể hiện lớp. Ngược lại, đối số đầu tiên MyClassMethod() Là liên kết với chính đối tượng lớp (ví dụ: trong trường hợp này, Test). Dành cho MyStaticMethod(), không có đối số nào bị ràng buộc và có đối số là tùy chọn.

"Biến tĩnh"

Tuy nhiên, triển khai "biến tĩnh" (tốt, có thể thay đổi biến tĩnh, dù sao, nếu đó không phải là mâu thuẫn về mặt ...) không thẳng thắn. Như millerdev chỉ ra trong câu trả lời của mình, vấn đề là 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à bởi vì dòng x.i = 12 đã thêm thuộc tính dụ mới i đến x thay vì thay đổi giá trị của Test lớp học i thuộc tính.

Một phần hành vi biến tĩnh dự kiến, tức là, đồng bộ hóa thuộc tính giữa nhiều phiên bản (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 chuyể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 sẽ vẫn được đồng bộ hóa giữa tất cả các phiên bản lớp.

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

Lưu ý rằng kỹ thuật nói, i vẫn không phải là 'biến tĩnh'; nó là một property, là một loại mô tả đặc biệt. Tuy nhiên, property hành vi hiện tại 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 phiên bản lớp.

"Biến tĩnh" không thể thay đổi

Đối với hành vi biến tĩnh không thay đổi, chỉ cần bỏ qua property setter:

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 thiết lập thể hiện i thuộc tính sẽ trả về AttributeError:

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

One Gotcha để Nhận thức được

Lưu ý rằng các phương thức trên chỉ hoạt động với các phiên bản của lớp học của bạn - họ sẽ không phải công việc 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 assert Test.i == x.i tạo ra một lỗi, bởi vì i thuộc tính của Test và x là hai đối tượng khác nhau.

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

    i = property(get_i) 

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

Nếu bạn thấy những điều khó hiểu ở trên, bạn có nhiều khả năng vẫn 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 bên dưới; tuy nhiên tôi sẽ đề nghị - một cách 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 hoàn toàn hiểu tại sao assert Test.i = x.i gâ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 ủng hộ nó như là một "giải pháp tốt". Tôi có những nghi ngờ của tôi về việc 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ể việc 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 Python hoạt động.

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

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 một metaclass

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 (tức 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ể xác định metaclass của riêng bạn 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ỉ có Python 3):

class MyClass(metaclass = MyMeta):
    pass

type(MyClass)  # class MyMeta

Dưới đây là một metaclass tôi đã tạo ra mà cố gắng để thi đua "biến tĩnh" hành vi 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 thuộc tính được yêu cầu có phải là một "biến tĩnh" hay không.

Một danh mục của "biến tĩnh" được lưu trữ trong StaticVarMeta.statics thuộ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 thứ tự độ phân giải thay thế. Tôi đã gọi đây là "thứ tự độ phân giải tĩnh" hoặc "SRO". Điều này được thực hiện bằng cách tìm thuộc tính được yêu cầu trong tập hợp các "biến tĩnh" cho một lớp đã cho (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ẽ quay trở lại thuộc tính mặc định của tập hợp get / set / delete (tức 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

141
2017-12-19 15:16



Tôi đã cố gắng để sử dụng theo cách của bạn nhưng tôi phải đối mặt với một vấn đề, vui lòng có một cái nhìn tại 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 mọi thứ bạn làm trên lớp Test (trước khi sử dụng nó cho instantiating trường hợp) như là trong lĩnh vực meta-lập trình? Ví dụ, bạn thay đổi hành vi của lớp bằng cách làm Test.i = 0 (ở đây bạn chỉ cần phá hủy hoàn toàn đối tượng thuộc tính). Tôi đoán các "cơ chế tài sản" đá chỉ trong tài sản truy cập vào trường hợp của một lớp học (trừ khi bạn thay đổi hành vi cơ bản bằng cách sử dụng một lớp meta như là một trung gian, có lẽ). Btw, xin vui lòng hoàn thành câu trả lời này :-) - Ole Thomsen Buus
@ RickTeachey Cảm ơn :-) metaclass của bạn cuối cùng là thú vị nhưng thực sự là một chút quá phức tạp cho ý thích của tôi. Nó có thể hữu ích trong một khung công tác / ứng dụng lớn, nơi mà cơ chế này là hoàn toàn cần thiết. Dù sao, điều này minh họa rằng nếu mới (phức tạp) không mặc định meta-hành vi là thực sự cần thiết, Python làm cho nó có thể :) - Ole Thomsen Buus
@OleThomsenBuus: Kiểm tra câu trả lời của tôi cho một metaclass đơn giản hơn mà thực hiện công việc. - Ethan Furman
@taper Bạn đúng; Tôi đã chỉnh sửa câu trả lời để khắc phục vấn đề (không thể tin rằng nó đã được đặt ở đó sai quá lâu!). Xin lỗi vì sự nhầm lẫn. - Rick Teachey


Bạn cũng có thể thêm các biến lớp vào các lớp khi đang di chuyển

>>> 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 cá thể 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]

23
2017-09-17 08:06



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
Vâng. Các lớp là các trình đơn có hiệu quả, bất kể không gian tên bạn gọi chúng là gì. - Pedro


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 pháp tĩnh. Chủ yếu là vì tôi có được lớp như một đố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 .. Thời gian bạn tìm kiếm một số định nghĩa python .. biến luôn luôn có thể thay đổi. Có hai loại của chúng có thể thay đổi và bất biến .. Ngoài ra, có các thuộc tính lớp và các thuộc tính instance .. Không có gì thực sự thích các thuộc tính tĩnh theo nghĩa java & c ++

Tại sao sử dụng phương pháp tĩnh trong ý nghĩa pythonic, nếu nó không có 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.


12
2017-09-16 02:02



Các biến không thể thay đổi hoặc không thay đổi được; đố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


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

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 tôi gặp lỗi. Điều này là do nó đòi hỏi phương thức đó được gọi trên một cá thể của lớp này. Phương pháp myStaticMethod được thiết lập như là một classmethod bằng cách sử dụng người trang trí  @classmethod.

Chỉ để đá 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 vào một thể hiện của lớp, như sau:

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

11
2017-09-16 02:05





Một điều đặc biệt cần lưu ý về các thuộc tính tĩnh và các thuộc tính cá thể, đượ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 instance, nếu chúng ta cố gắng truy cập thuộc tính thông qua 'instance, 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ớ.


10
2018-03-08 06:06





Khi xác định 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 phụ thuộc vào cách biến được biểu diễ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 định nghĩa.

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

7
2018-03-26 17:56





Bạn cũng có thể thực thi một lớp là tĩnh bằng cách sử dụng metaclass.

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 do tai nạn bạn cố gắng khởi tạo Lớp học của tôi bạn sẽ nhận được một StaticClassError.


6
2017-11-20 12:06



Tại sao nó thậm chí là một lớp học nếu bạn không định tạo nó? Điều này cảm thấy giống như xoắn Python để biến nó thành Java .... - Ned Batchelder
Các Thành ngữ Borg là cách tốt hơn để xử lý việc này. - Rick Teachey


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

Dưới đâ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 tinh chỉnh để khớp với bất kỳ đ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

4
2018-03-25 09:02



Điều này đơn giản hơn nhiều so với nỗ lực của tôi ở trên. Bravo! - Rick Teachey


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 ra "ảo biến":

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ỳ bài tập nào cho chúng sau khi chúng được tạo ra. Lưu ý rằng việc sử dụng tra cứu self bởi vì, mặc dù label là tĩnh theo nghĩa là không được liên kết với cụ thể Ví dụ, giá trị vẫn phụ thuộc vào thể hiện (lớp của).


4
2017-09-21 04:04