Thuộc tính chỉ đọc trong Python


95

Tôi không biết khi nào thuộc tính nên là riêng tư và liệu tôi có nên sử dụng thuộc tính hay không.

Tôi đã đọc gần đây rằng setters và getters không phải là pythonic và tôi nên sử dụng trình trang trí tài sản. Được rồi.

Nhưng nếu tôi có thuộc tính thì sao, thuộc tính đó không được đặt từ bên ngoài lớp mà có thể được đọc (thuộc tính chỉ đọc). Thuộc tính này có nên ở chế độ riêng tư, và ý tôi là riêng tư với dấu gạch dưới, như vậy self._x? Nếu có thì làm thế nào tôi có thể đọc nó mà không sử dụng getter? Phương pháp duy nhất tôi biết bây giờ là viết

@property
def x(self):
    return self._x

Bằng cách đó, tôi có thể đọc thuộc tính của obj.xnó nhưng tôi không thể đặt nó obj.x = 1nên không sao cả.

Nhưng tôi có nên thực sự quan tâm đến việc thiết lập đối tượng không được đặt không? Có lẽ tôi nên để nó đi. Nhưng một lần nữa, tôi không thể sử dụng dấu gạch dưới vì việc đọc obj._xlà kỳ lạ đối với người dùng, vì vậy tôi nên sử dụng obj.xvà sau đó người dùng không biết rằng anh ta không được đặt thuộc tính này.

Ý kiến ​​và thực hành của bạn là gì?


1
Ý tưởng của một thuộc tính là nó hoạt động giống như một thuộc tính nhưng có thể có thêm mã. Nếu tất cả những gì bạn muốn là nhận được một giá trị, tôi thậm chí sẽ không bận tâm: chỉ cần sử dụng self.xvà tin tưởng rằng không ai sẽ thay đổi x. Nếu đảm bảo rằng điều đó xkhông thể thay đổi là quan trọng, thì hãy sử dụng thuộc tính.
li.davidm

Ngoài ra, _xkhông lạ chút nào: theo quy ước , nó có nghĩa là một cái gì đó "riêng tư".
li.davidm

1
Ý tôi là việc đọc từ _x là kỳ quặc. Không phải tên _x. Nếu người dùng đang đọc trực tiếp từ _x thì anh ta không chịu trách nhiệm.
Rafał Łużyński

3
Quan trọng! Lớp của bạn phải là một lớp kiểu mới, tức là kế thừa từ object, vì điều này thực sự ngăn bạn thiết lập obj.x. Trên một lớp kiểu cũ, bạn vẫn có thể thiết lập obj.x, với kết quả khá bất ngờ.
Ian H

Có một số lý do hợp lệ để có thuộc tính chỉ đọc. Một là khi bạn có một giá trị bao gồm việc hợp nhất hai giá trị (đọc / ghi) khác. Bạn có thể làm điều này trong một phương thức, nhưng bạn cũng có thể thực hiện nó trong một thuộc tính chỉ đọc.
philologon

Câu trả lời:


68

Nói chung, các chương trình Python nên được viết với giả định rằng tất cả người dùng đều là người lớn đồng ý và do đó tự chịu trách nhiệm sử dụng mọi thứ một cách chính xác. Tuy nhiên, trong một số trường hợp hiếm hoi khi một thuộc tính không thể thiết lập được (chẳng hạn như giá trị dẫn xuất hoặc giá trị được đọc từ một số nguồn dữ liệu tĩnh), thuộc tính getter-only thường là mẫu được ưu tiên.


26
Có vẻ như câu trả lời của bạn mâu thuẫn với chính nó. Bạn nói rằng người dùng nên có trách nhiệm và sử dụng mọi thứ một cách chính xác, sau đó bạn nói rằng đôi khi không có ý nghĩa khi một thuộc tính có thể thiết lập được và thuộc tính getter là một cách được ưu tiên. Theo tôi bạn có thể hoặc không thể đặt thuộc tính. Câu hỏi duy nhất là liệu tôi có nên bảo vệ phần đính kèm này hay bỏ nó đi. Không nên có câu trả lời ở giữa.
Rafał Łużyński

19
Không, tôi đã nói nếu bạn thực sự không thể đặt một giá trị, thì việc có một bộ định giá không có ý nghĩa gì. Ví dụ: nếu bạn có một đối tượng hình tròn với một phần tử bán kính và một thuộc tính chu vi có nguồn gốc từ bán kính hoặc bạn có một đối tượng bao bọc một số api thời gian thực chỉ đọc với một số thuộc tính chỉ getter. Không có gì mâu thuẫn với bất cứ điều gì.
Silas Ray,

9
Nhưng người dùng có trách nhiệm sẽ không cố gắng đặt attr mà theo nghĩa đen là không thể đặt được. Và một lần nữa, người dùng không có trách nhiệm sẽ đặt attr mà theo nghĩa đen có thể được đặt và sẽ phát sinh lỗi ở một nơi khác trong mã do bộ của anh ta. Vì vậy, cuối cùng cả hai attr không thể được thiết lập. Tôi có nên sử dụng thuộc tính trên cả hai hay không sử dụng nó trên bất kỳ?
Rafał Łużyński

8
Nhưng người dùng có trách nhiệm không nên cố gắng đặt attr mà theo nghĩa đen là không thể đặt được. Trong lập trình, nếu một cái gì đó hoàn toàn là một giá trị không thể đặt được, thì điều có trách nhiệm hoặc hợp lý là đảm bảo rằng nó không thể có được. Những điều nhỏ nhặt này đều góp phần tạo nên những chương trình đáng tin cậy.
Robin Smith

6
Đó là một vị trí mà nhiều người và nhiều ngôn ngữ đảm nhận. Nếu đó là một vị trí mà bạn thấy không thể thương lượng, có lẽ bạn không nên sử dụng Python.
Silas Ray

72

Chỉ hai xu của tôi, Silas Ray đang đi đúng hướng, tuy nhiên tôi cảm thấy muốn thêm một ví dụ. ;-)

Python là một ngôn ngữ không an toàn về kiểu chữ và do đó bạn sẽ luôn phải tin tưởng những người dùng mã của mình để sử dụng mã như một người hợp lý (hợp lý).

Mỗi PEP 8 :

Chỉ sử dụng một dấu gạch dưới ở đầu cho các phương thức không công khai và biến phiên bản.

Để có thuộc tính 'chỉ đọc' trong một lớp mà bạn có thể sử dụng @propertytrang trí, bạn sẽ cần kế thừa từ objectkhi làm như vậy để sử dụng các lớp kiểu mới.

Thí dụ:

>>> class A(object):
...     def __init__(self, a):
...         self._a = a
...
...     @property
...     def a(self):
...         return self._a
... 
>>> a = A('test')
>>> a.a
'test'
>>> a.a = 'pleh'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute

9
Python không phải là kiểu không an toàn, nó được nhập động. Và việc xáo trộn tên không phải để làm cho việc gian lận trở nên khó khăn hơn, mà là để ngăn chặn các cuộc đụng độ tên trong các tình huống mà việc thừa kế có thể có vấn đề (nếu bạn không lập trình lớn, bạn thậm chí không nên quan tâm).
memeplex

3
Nhưng bạn vẫn nên nhớ rằng các đối tượng có thể thay đổi có thể được thay đổi bằng cách sử dụng phương pháp này. Ví dụ self.__a = [], nếu , bạn vẫn có thể làm điều này a.a.append('anything')và nó sẽ hoạt động.
Igor

3
Tôi không rõ ràng là "một người hợp lý (hợp lý)" có gì trong câu trả lời này. Bạn có thể nói rõ hơn về những điều bạn nghĩ một người hợp lý sẽ và sẽ không làm không?
winni2k

3
Đối với tôi, việc sử dụng trang trí @property, bạn sẽ cần kế thừa từ đối tượng khi bạn làm như vậy là toàn bộ điểm cho câu trả lời này. Cảm ơn.
akki

2
@kkm cách duy nhất để không bao giờ cho phép lỗi xâm nhập vào mã là không bao giờ viết mã.
Alechan

55

Đây là một cách để tránh giả định rằng

tất cả người dùng đều là người lớn đồng ý và do đó có trách nhiệm tự mình sử dụng mọi thứ một cách chính xác.

vui lòng xem cập nhật của tôi bên dưới

Sử dụng @property, rất dài dòng, ví dụ:

   class AClassWithManyAttributes:
        '''refactored to properties'''
        def __init__(a, b, c, d, e ...)
             self._a = a
             self._b = b
             self._c = c
             self.d = d
             self.e = e

        @property
        def a(self):
            return self._a
        @property
        def b(self):
            return self._b
        @property
        def c(self):
            return self._c
        # you get this ... it's long

Sử dụng

Không có gạch dưới: đó là một biến công khai.
Một dấu gạch dưới: đó là một biến được bảo vệ.
Hai dấu gạch dưới: đó là một biến riêng.

Ngoại trừ cái cuối cùng, đó là một quy ước. Bạn vẫn có thể, nếu bạn thực sự cố gắng, truy cập các biến có dấu gạch dưới kép.

Vậy ta phải làm sao? Chúng ta có từ bỏ việc có thuộc tính chỉ đọc trong Python không?

Hãy chứng kiến! read_only_propertiestrang trí để giải cứu!

@read_only_properties('readonly', 'forbidden')
class MyClass(object):
    def __init__(self, a, b, c):
        self.readonly = a
        self.forbidden = b
        self.ok = c

m = MyClass(1, 2, 3)
m.ok = 4
# we can re-assign a value to m.ok
# read only access to m.readonly is OK 
print(m.ok, m.readonly) 
print("This worked...")
# this will explode, and raise AttributeError
m.forbidden = 4

Bạn hỏi:

Đâu là read_only_propertiesđến từ đâu?

Rất vui vì bạn đã hỏi, đây là nguồn cho read_only_properties :

def read_only_properties(*attrs):

    def class_rebuilder(cls):
        "The class decorator"

        class NewClass(cls):
            "This is the overwritten class"
            def __setattr__(self, name, value):
                if name not in attrs:
                    pass
                elif name not in self.__dict__:
                    pass
                else:
                    raise AttributeError("Can't modify {}".format(name))

                super().__setattr__(name, value)
        return NewClass
    return class_rebuilder

cập nhật

Tôi không bao giờ mong đợi câu trả lời này sẽ nhận được nhiều sự chú ý như vậy. Đáng ngạc nhiên là nó không. Điều này khuyến khích tôi tạo một gói bạn có thể sử dụng.

$ pip install read-only-properties

trong vỏ trăn của bạn:

In [1]: from rop import read_only_properties

In [2]: @read_only_properties('a')
   ...: class Foo:
   ...:     def __init__(self, a, b):
   ...:         self.a = a
   ...:         self.b = b
   ...:         

In [3]: f=Foo('explodes', 'ok-to-overwrite')

In [4]: f.b = 5

In [5]: f.a = 'boom'
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-5-a5226072b3b4> in <module>()
----> 1 f.a = 'boom'

/home/oznt/.virtualenvs/tracker/lib/python3.5/site-packages/rop.py in __setattr__(self, name, value)
    116                     pass
    117                 else:
--> 118                     raise AttributeError("Can't touch {}".format(name))
    119 
    120                 super().__setattr__(name, value)

AttributeError: Can't touch a

1
Điều này thực sự hữu ích và thực hiện chính xác những gì tôi muốn làm. Cảm ơn bạn. Tuy nhiên, nó dành cho những người đã cài đặt Python 3. Tôi đang sử dụng Python 2.7.8, vì vậy tôi phải áp dụng hai chỉnh sửa nhỏ cho giải pháp của bạn: "class NewClass (cls, <b> object <\ b>):" ... "<b> super (NewClass, self) <\ b> .__ setattr __ (tên, giá trị) ".
Ying Zhang,

1
Ngoài ra, nên cẩn thận về các biến thành viên của lớp là danh sách và từ điển. Bạn thực sự không thể 'khóa chúng' khỏi việc cập nhật theo cách này.
Ying Zhang,

1
Một cải tiến và ba vấn đề ở đây. Cải tiến: if..elif..elsekhối có thể if name in attrs and name in self.__dict__: raise Attr...không passcần thiết. Vấn đề 1: các lớp do đó được trang trí tất cả đều giống nhau __name__và biểu diễn chuỗi của kiểu của chúng cũng được đồng nhất. Vấn đề 2: trang trí này ghi đè lên bất kỳ tùy chỉnh nào __setattr__. Vấn đề 3: người dùng có thể đánh bại điều này với del MyClass.__setattr__.
TigerhawkT3

Chỉ là một thứ ngôn ngữ. "Chao ôi ..." có nghĩa là "Buồn phải nói, ..." đó không phải là điều bạn muốn, tôi nghĩ.
Thomas Andrews,

Không có gì ngăn cản tôi làm object.__setattr__(f, 'forbidden', 42). Tôi không thấy những gì read_only_propertiesthêm mà không được xử lý bởi tên gạch dưới kép mangling.
L3viathan

4

Đây là một cách tiếp cận hơi khác đối với các thuộc tính chỉ đọc, mà có lẽ nên được gọi là thuộc tính ghi một lần vì chúng phải được khởi tạo, phải không? Đối với những người hoang tưởng trong số chúng ta, những người lo lắng về việc có thể sửa đổi các thuộc tính bằng cách truy cập trực tiếp vào từ điển của đối tượng, tôi đã giới thiệu tên mangling "cực đoan":

from uuid import uuid4

class Read_Only_Property:
    def __init__(self, name):
        self.name = name
        self.dict_name = uuid4().hex
        self.initialized = False

    def __get__(self, instance, cls):
        if instance is None:
            return self
        else:
            return instance.__dict__[self.dict_name]

    def __set__(self, instance, value):
        if self.initialized:
            raise AttributeError("Attempt to modify read-only property '%s'." % self.name)
        instance.__dict__[self.dict_name] = value
        self.initialized = True

class Point:
    x = Read_Only_Property('x')
    y = Read_Only_Property('y')
    def __init__(self, x, y):
        self.x = x
        self.y = y

if __name__ == '__main__':
    try:
        p = Point(2, 3)
        print(p.x, p.y)
        p.x = 9
    except Exception as e:
        print(e)

Đẹp. Nếu bạn mangle dict_namethay vào đó, ví dụ: dict_name = "_spam_" + namenó sẽ loại bỏ sự phụ thuộc vào uuid4và làm cho việc gỡ lỗi dễ dàng hơn rất nhiều.
cz

Nhưng sau đó tôi có thể nói p.__dict__['_spam_x'] = 5để thay đổi giá trị của p.x, vì vậy điều này không cung cấp đủ tên miền.
Booboo

1

Tôi không hài lòng với hai câu trả lời trước để tạo thuộc tính chỉ đọc vì giải pháp đầu tiên cho phép xóa thuộc tính chỉ đọc, sau đó đặt và không chặn __dict__. Giải pháp thứ hai có thể được giải quyết với thử nghiệm - tìm giá trị bằng với những gì bạn đặt nó hai và thay đổi nó cuối cùng.

Bây giờ, đối với mã.

def final(cls):
    clss = cls
    @classmethod
    def __init_subclass__(cls, **kwargs):
        raise TypeError("type '{}' is not an acceptable base type".format(clss.__name__))
    cls.__init_subclass__ = __init_subclass__
    return cls


def methoddefiner(cls, method_name):
    for clss in cls.mro():
        try:
            getattr(clss, method_name)
            return clss
        except(AttributeError):
            pass
    return None


def readonlyattributes(*attrs):
    """Method to create readonly attributes in a class

    Use as a decorator for a class. This function takes in unlimited 
    string arguments for names of readonly attributes and returns a
    function to make the readonly attributes readonly. 

    The original class's __getattribute__, __setattr__, and __delattr__ methods
    are redefined so avoid defining those methods in the decorated class

    You may create setters and deleters for readonly attributes, however
    if they are overwritten by the subclass, they lose access to the readonly
    attributes. 

    Any method which sets or deletes a readonly attribute within
    the class loses access if overwritten by the subclass besides the __new__
    or __init__ constructors.

    This decorator doesn't support subclassing of these classes
    """
    def classrebuilder(cls):
        def __getattribute__(self, name):
            if name == '__dict__':
                    from types import MappingProxyType
                    return MappingProxyType(super(cls, self).__getattribute__('__dict__'))
            return super(cls, self).__getattribute__(name)
        def __setattr__(self, name, value): 
                if name == '__dict__' or name in attrs:
                    import inspect
                    stack = inspect.stack()
                    try:
                        the_class = stack[1][0].f_locals['self'].__class__
                    except(KeyError):
                        the_class = None
                    the_method = stack[1][0].f_code.co_name
                    if the_class != cls: 
                         if methoddefiner(type(self), the_method) != cls:
                            raise AttributeError("Cannot set readonly attribute '{}'".format(name))                        
                return super(cls, self).__setattr__(name, value)
        def __delattr__(self, name):                
                if name == '__dict__' or name in attrs:
                    import inspect
                    stack = inspect.stack()
                    try:
                        the_class = stack[1][0].f_locals['self'].__class__
                    except(KeyError):
                        the_class = None
                    the_method = stack[1][0].f_code.co_name
                    if the_class != cls:
                        if methoddefiner(type(self), the_method) != cls:
                            raise AttributeError("Cannot delete readonly attribute '{}'".format(name))                        
                return super(cls, self).__delattr__(name)
        clss = cls
        cls.__getattribute__ = __getattribute__
        cls.__setattr__ = __setattr__
        cls.__delattr__ = __delattr__
        #This line will be moved when this algorithm will be compatible with inheritance
        cls = final(cls)
        return cls
    return classrebuilder

def setreadonlyattributes(cls, *readonlyattrs):
    return readonlyattributes(*readonlyattrs)(cls)


if __name__ == '__main__':
    #test readonlyattributes only as an indpendent module
    @readonlyattributes('readonlyfield')
    class ReadonlyFieldClass(object):
        def __init__(self, a, b):
            #Prevent initalization of the internal, unmodified PrivateFieldClass
            #External PrivateFieldClass can be initalized
            self.readonlyfield = a
            self.publicfield = b


    attr = None
    def main():
        global attr
        pfi = ReadonlyFieldClass('forbidden', 'changable')
        ###---test publicfield, ensure its mutable---###
        try:
            #get publicfield
            print(pfi.publicfield)
            print('__getattribute__ works')
            #set publicfield
            pfi.publicfield = 'mutable'
            print('__setattr__ seems to work')
            #get previously set publicfield
            print(pfi.publicfield)
            print('__setattr__ definitely works')
            #delete publicfield
            del pfi.publicfield 
            print('__delattr__ seems to work')
            #get publicfield which was supposed to be deleted therefore should raise AttributeError
            print(pfi.publlicfield)
            #publicfield wasn't deleted, raise RuntimeError
            raise RuntimeError('__delattr__ doesn\'t work')
        except(AttributeError):
            print('__delattr__ works')


        try:
            ###---test readonly, make sure its readonly---###
            #get readonlyfield
            print(pfi.readonlyfield)
            print('__getattribute__ works')
            #set readonlyfield, should raise AttributeError
            pfi.readonlyfield = 'readonly'
            #apparently readonlyfield was set, notify user
            raise RuntimeError('__setattr__ doesn\'t work')
        except(AttributeError):
            print('__setattr__ seems to work')
            try:
                #ensure readonlyfield wasn't set
                print(pfi.readonlyfield)
                print('__setattr__ works')
                #delete readonlyfield
                del pfi.readonlyfield
                #readonlyfield was deleted, raise RuntimeError
                raise RuntimeError('__delattr__ doesn\'t work')
            except(AttributeError):
                print('__delattr__ works')
        try:
            print("Dict testing")
            print(pfi.__dict__, type(pfi.__dict__))
            attr = pfi.readonlyfield
            print(attr)
            print("__getattribute__ works")
            if pfi.readonlyfield != 'forbidden':
                print(pfi.readonlyfield)
                raise RuntimeError("__getattr__ doesn't work")
            try:
                pfi.__dict__ = {}
                raise RuntimeError("__setattr__ doesn't work")
            except(AttributeError):
                print("__setattr__ works")
            del pfi.__dict__
            raise RuntimeError("__delattr__ doesn't work")
        except(AttributeError):
            print(pfi.__dict__)
            print("__delattr__ works")
            print("Basic things work")


main()

Không có lý do gì để tạo thuộc tính chỉ đọc ngoại trừ khi mã thư viện đang viết của bạn , mã đang được phân phối cho người khác dưới dạng mã để sử dụng nhằm nâng cao chương trình của họ, không phải mã cho bất kỳ mục đích nào khác, như phát triển ứng dụng. Vấn đề __dict__ đã được giải quyết, vì __dict__ bây giờ thuộc loại bất biến.MappingProxyType , vì vậy các thuộc tính không thể thay đổi thông qua __dict__. Cài đặt hoặc xóa __dict__ cũng bị chặn. Cách duy nhất để thay đổi các thuộc tính chỉ đọc là thông qua việc thay đổi các phương thức của chính lớp đó.

Mặc dù tôi tin rằng giải pháp của tôi tốt hơn hai giải pháp trước, nhưng nó có thể được cải thiện. Đây là những điểm yếu của mã này:

a) Không cho phép thêm vào một phương thức trong một lớp con để đặt hoặc xóa một thuộc tính chỉ đọc. Một phương thức được định nghĩa trong lớp con sẽ tự động bị cấm truy cập vào thuộc tính chỉ đọc, ngay cả khi gọi phiên bản của phương thức này.

b) Các phương thức chỉ đọc của lớp 'có thể được thay đổi để đánh bại các hạn chế chỉ đọc.

Tuy nhiên, không có cách nào mà không chỉnh sửa lớp để đặt hoặc xóa thuộc tính chỉ đọc. Điều này không phụ thuộc vào quy ước đặt tên, điều này tốt vì Python không nhất quán với quy ước đặt tên. Điều này cung cấp một cách để thực hiện các thuộc tính chỉ đọc mà không thể thay đổi với các sơ hở ẩn mà không cần chỉnh sửa chính lớp đó. Đơn giản chỉ cần liệt kê các thuộc tính chỉ được đọc khi gọi trình trang trí dưới dạng đối số và chúng sẽ trở thành chỉ đọc.

Ghi nhận câu trả lời của Brice trong Cách lấy tên lớp người gọi bên trong một hàm của lớp khác trong python? để lấy các lớp và phương thức của người gọi.


object.__setattr__(pfi, 'readonly', 'foobar')phá vỡ giải pháp này mà không cần chỉnh sửa chính lớp đó.
L3viathan

0

Lưu ý rằng các phương thức thể hiện cũng là các thuộc tính (của lớp) và bạn có thể đặt chúng ở cấp độ lớp hoặc cấp độ cá thể nếu bạn thực sự muốn trở thành một badass. Hoặc bạn có thể đặt một biến lớp (cũng là một thuộc tính của lớp), trong đó các thuộc tính chỉ đọc tiện dụng sẽ không hoạt động gọn gàng. Điều tôi đang cố gắng nói là vấn đề "thuộc tính chỉ đọc" trên thực tế tổng quát hơn so với người ta thường thấy. May mắn thay, có những kỳ vọng thông thường tại nơi làm việc mạnh đến mức khiến chúng ta mù quáng trong những trường hợp khác này (sau tất cả, hầu hết mọi thứ đều là một thuộc tính của một số loại trong python).

Dựa trên những kỳ vọng này, tôi nghĩ rằng cách tiếp cận chung nhất và nhẹ nhàng nhất là áp dụng quy ước rằng các thuộc tính "public" (không có dấu gạch dưới ở đầu) chỉ được đọc trừ khi được ghi rõ ràng là có thể ghi. Điều này phụ thuộc vào kỳ vọng thông thường rằng các phương thức sẽ không được vá và các biến lớp chỉ ra giá trị mặc định của phiên bản tốt hơn. Nếu bạn cảm thấy thực sự hoang tưởng về một số thuộc tính đặc biệt, hãy sử dụng bộ mô tả chỉ đọc làm thước đo tài nguyên cuối cùng.


0

Mặc dù tôi thích trình trang trí lớp từ Oz123, bạn cũng có thể làm như sau, sử dụng trình bao bọc lớp rõ ràng và __new__ với phương thức Nhà máy của lớp trả về lớp trong một bao đóng:

class B(object):
    def __new__(cls, val):
        return cls.factory(val)

@classmethod
def factory(cls, val):
    private = {'var': 'test'}

    class InnerB(object):
        def __init__(self):
            self.variable = val
            pass

        @property
        def var(self):
            return private['var']

    return InnerB()

bạn nên thêm một số thử nghiệm cho thấy nó hoạt động như thế nào với nhiều thuộc tính
Oz123

0

Đó là cách giải quyết của tôi.

@property
def language(self):
    return self._language
@language.setter
def language(self, value):
    # WORKAROUND to get a "getter-only" behavior
    # set the value only if the attribute does not exist
    try:
        if self.language == value:
            pass
        print("WARNING: Cannot set attribute \'language\'.")
    except AttributeError:
        self._language = value

0

ai đó đã đề cập đến việc sử dụng đối tượng proxy, tôi không thấy ví dụ về điều đó nên cuối cùng tôi đã thử nó, [tội nghiệp].

/! \ Hãy thích các định nghĩa lớp và các hàm tạo lớp nếu có thể

mã này được viết lại một cách hiệu quả class.__new__(phương thức tạo lớp) ngoại trừ tệ hơn theo mọi cách. Hãy tự cứu mình và đừng sử dụng mẫu này nếu bạn có thể.

def attr_proxy(obj):
    """ Use dynamic class definition to bind obj and proxy_attrs.
        If you can extend the target class constructor that is 
        cleaner, but its not always trivial to do so.
    """
    proxy_attrs = dict()

    class MyObjAttrProxy():
        def __getattr__(self, name):
            if name in proxy_attrs:
                return proxy_attrs[name]  # overloaded

            return getattr(obj, name)  # proxy

        def __setattr__(self, name, value):
            """ note, self is not bound when overloading methods
            """
            proxy_attrs[name] = value

    return MyObjAttrProxy()


myobj = attr_proxy(Object())
setattr(myobj, 'foo_str', 'foo')

def func_bind_obj_as_self(func, self):
    def _method(*args, **kwargs):
        return func(self, *args, **kwargs)
    return _method

def mymethod(self, foo_ct):
    """ self is not bound because we aren't using object __new__
        you can write the __setattr__ method to bind a self 
        argument, or declare your functions dynamically to bind in 
        a static object reference.
    """
    return self.foo_str + foo_ct

setattr(myobj, 'foo', func_bind_obj_as_self(mymethod, myobj))

-2

Tôi biết tôi đang mang chủ đề này trở lại từ cõi chết, nhưng tôi đang tìm cách làm cho thuộc tính chỉ đọc và sau khi tìm thấy chủ đề này, tôi không hài lòng với các giải pháp đã được chia sẻ.

Vì vậy, quay trở lại câu hỏi ban đầu, nếu bạn bắt đầu với mã này:

@property
def x(self):
    return self._x

Và bạn muốn làm cho X chỉ đọc, bạn có thể thêm:

@x.setter
def x(self, value):
    raise Exception("Member readonly")

Sau đó, nếu bạn chạy như sau:

print (x) # Will print whatever X value is
x = 3 # Will raise exception "Member readonly"

3
Nhưng nếu bạn chỉ không làm cho một setter, cố gắng để assign sẽ nâng cao một lỗi cũng (An AttributeError('can't set attribute'))
Artyer
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.