Hướng dẫn python getters and setters vs property - python getters và setters so với thuộc tính

[Tl; dr? Bạn có thể bỏ qua kết thúc cho một ví dụ mã.]TL;DR? You can skip to the end for a code example.]

Tôi thực sự thích sử dụng một thành ngữ khác, có một chút liên quan đến việc sử dụng như một lần tắt, nhưng thật tuyệt nếu bạn có trường hợp sử dụng phức tạp hơn.

Một chút nền trước.

Các thuộc tính hữu ích ở chỗ chúng cho phép chúng tôi xử lý cả cài đặt và nhận các giá trị theo cách lập trình nhưng vẫn cho phép các thuộc tính được truy cập làm thuộc tính. Chúng ta có thể biến 'nhận' thành 'tính toán' (về cơ bản) và chúng ta có thể biến 'bộ' thành 'sự kiện'. Vì vậy, giả sử chúng tôi có lớp sau, mà tôi đã mã hóa với các getters và setters giống Java.

class Example(object):
    def __init__(self, x=None, y=None):
        self.x = x
        self.y = y

    def getX(self):
        return self.x or self.defaultX()

    def getY(self):
        return self.y or self.defaultY()

    def setX(self, x):
        self.x = x

    def setY(self, y):
        self.y = y

    def defaultX(self):
        return someDefaultComputationForX()

    def defaultY(self):
        return someDefaultComputationForY()

Bạn có thể tự hỏi tại sao tôi không gọi

class Example(object):
    def __init__(self, x=None, y=None):
        self._x = x
        self._y = y

    @property
    def x(self):
        return self.x or self.defaultX()

    @x.setter
    def x(self, value):
        self._x = value

    @property
    def y(self):
        return self.y or self.defaultY()

    @y.setter
    def y(self, value):
        self._y = value

    # default{XY} as before.
9 và
e.x[a,b,c] = 10
e.x[d,e,f] = 20
0 trong phương thức
e.x[a,b,c] = 10
e.x[d,e,f] = 20
1 của đối tượng. Lý do là vì trường hợp của chúng tôi, tôi muốn cho rằng các phương thức
e.x[a,b,c] = 10
e.x[d,e,f] = 20
2 trả về các giá trị thay đổi theo thời gian, hãy nói một dấu thời gian và bất cứ khi nào
e.x[a,b,c] = 10
e.x[d,e,f] = 20
3 (hoặc
e.x[a,b,c] = 10
e.x[d,e,f] = 20
4) không được đặt ra (trong đó, cho mục đích của ví dụ này, "không đặt" có nghĩa là "đặt thành none") Tôi muốn giá trị tính toán mặc định của ____ 23 (hoặc ________ 24).

Vì vậy, điều này là khập khiễng vì một số lý do mô tả ở trên. Tôi sẽ viết lại nó bằng các thuộc tính:

class Example(object):
    def __init__(self, x=None, y=None):
        self._x = x
        self._y = y

    @property
    def x(self):
        return self.x or self.defaultX()

    @x.setter
    def x(self, value):
        self._x = value

    @property
    def y(self):
        return self.y or self.defaultY()

    @y.setter
    def y(self, value):
        self._y = value

    # default{XY} as before.

Chúng ta đã đạt được gì? Chúng tôi đã đạt được khả năng gọi các thuộc tính này là thuộc tính mặc dù, đằng sau hậu trường, cuối cùng chúng tôi đã chạy các phương thức.

Tất nhiên, sức mạnh thực sự của các thuộc tính là chúng ta thường muốn các phương thức này làm một cái gì đó ngoài việc chỉ nhận và thiết lập các giá trị (nếu không thì không có điểm nào trong việc sử dụng các thuộc tính). Tôi đã làm điều này trong ví dụ getter của tôi. Về cơ bản, chúng tôi đang chạy một thân chức năng để chọn mặc định bất cứ khi nào giá trị không được đặt. Đây là một mô hình rất phổ biến.

Nhưng chúng ta đang mất gì, và chúng ta không thể làm gì?

Điều khó chịu chính, theo quan điểm của tôi, là nếu bạn xác định một getter (như chúng tôi làm ở đây), bạn cũng phải xác định một setter. [1] Đó là tiếng ồn thêm mà củng cố mã.

Một phiền toái khác là chúng ta vẫn phải khởi tạo các giá trị

e.x[a,b,c] = 10
e.x[d,e,f] = 20
3 và
e.x[a,b,c] = 10
e.x[d,e,f] = 20
4 trong
e.x[a,b,c] = 10
e.x[d,e,f] = 20
1. (Tất nhiên, chúng tôi có thể thêm chúng bằng cách sử dụng
e.x = [a,b,c,10]
e.x = [d,e,f,30]
0 nhưng đó là mã bổ sung hơn.)

Thứ ba, không giống như trong ví dụ giống Java, Getters không thể chấp nhận các tham số khác. Bây giờ tôi có thể nghe thấy bạn đã nói, tốt, nếu đó là tham số thì đó không phải là một người nhận! Trong một ý nghĩa chính thức, đó là sự thật. Nhưng theo nghĩa thực tế, không có lý do gì chúng ta không thể tham số hóa một thuộc tính được đặt tên - như

e.x[a,b,c] = 10
e.x[d,e,f] = 20
3 - và đặt giá trị của nó cho một số tham số cụ thể.

Thật tuyệt nếu chúng ta có thể làm điều gì đó như:

e.x[a,b,c] = 10
e.x[d,e,f] = 20

Ví dụ. Gần nhất chúng ta có thể nhận được là ghi đè nhiệm vụ để ngụ ý một số ngữ nghĩa đặc biệt:

e.x = [a,b,c,10]
e.x = [d,e,f,30]

Và tất nhiên đảm bảo rằng setter của chúng tôi biết cách trích xuất ba giá trị đầu tiên làm khóa cho từ điển và đặt giá trị của nó thành một số hoặc một cái gì đó.

Nhưng ngay cả khi chúng tôi đã làm điều đó, chúng tôi vẫn không thể hỗ trợ nó với các thuộc tính vì không có cách nào để có được giá trị vì chúng tôi không thể chuyển các tham số cho getter. Vì vậy, chúng tôi đã phải trả lại tất cả mọi thứ, giới thiệu một sự bất đối xứng.

Getter/setter kiểu Java cho phép chúng tôi xử lý việc này, nhưng chúng tôi sẽ trở lại để cần getter/setters.

Trong tâm trí của tôi những gì chúng tôi thực sự muốn là một cái gì đó nắm bắt các yêu cầu sau:

  • Người dùng chỉ xác định một phương thức cho một thuộc tính nhất định và có thể cho biết có thuộc tính chỉ đọc hay đọc-viết. Thuộc tính Thất bại Thử nghiệm này nếu thuộc tính có thể ghi.

  • Người dùng không cần phải xác định một biến bổ sung bên dưới chức năng, vì vậy chúng tôi không cần

    e.x[a,b,c] = 10
    e.x[d,e,f] = 20
    
    1 hoặc
    e.x = [a,b,c,10]
    e.x = [d,e,f,30]
    
    3 trong mã. Biến chỉ tồn tại bởi thực tế chúng tôi đã tạo ra thuộc tính kiểu mới này.

  • Bất kỳ mã mặc định nào cho thuộc tính thực thi trong phần thân phương thức.

  • Chúng ta có thể đặt thuộc tính làm thuộc tính và tham chiếu nó là một thuộc tính.

  • Chúng ta có thể tham số hóa thuộc tính.

Về mặt mã, chúng tôi muốn một cách để viết:

def x(self, *args):
    return defaultX()

và có thể làm sau đó:

print e.x     -> The default at time T0
e.x = 1
print e.x     -> 1
e.x = None
print e.x     -> The default at time T1

và kể từ đó trở đi.

Chúng tôi cũng muốn một cách để làm điều này cho trường hợp đặc biệt của thuộc tính tham số hóa, nhưng vẫn cho phép trường hợp gán mặc định hoạt động. Bạn sẽ thấy cách tôi giải quyết điều này dưới đây.

Bây giờ đến điểm (yay! Điểm!). Giải pháp tôi đưa ra cho điều này là như sau.

Chúng tôi tạo một đối tượng mới để thay thế khái niệm của một tài sản. Đối tượng được dự định để lưu trữ giá trị của một biến được đặt cho nó, nhưng cũng duy trì một xử lý mã để biết cách tính một mặc định. Công việc của nó là lưu trữ bộ

e.x = [a,b,c,10]
e.x = [d,e,f,30]
4 hoặc chạy
e.x = [a,b,c,10]
e.x = [d,e,f,30]
5 nếu giá trị đó không được đặt.

Hãy gọi nó là

e.x = [a,b,c,10]
e.x = [d,e,f,30]
6.

class UberProperty(object):

    def __init__(self, method):
        self.method = method
        self.value = None
        self.isSet = False

    def setValue(self, value):
        self.value = value
        self.isSet = True

    def clearValue(self):
        self.value = None
        self.isSet = False

Tôi giả sử

e.x = [a,b,c,10]
e.x = [d,e,f,30]
5 Đây là một phương thức lớp,
e.x = [a,b,c,10]
e.x = [d,e,f,30]
4 là giá trị của
e.x = [a,b,c,10]
e.x = [d,e,f,30]
6 và tôi đã thêm
def x(self, *args):
    return defaultX()
0 vì
def x(self, *args):
    return defaultX()
1 có thể là một giá trị thực và điều này cho phép chúng tôi một cách sạch sẽ tuyên bố thực sự không có giá trị ". Một cách khác là một sentinel của một số loại.

Điều này về cơ bản cung cấp cho chúng tôi một đối tượng có thể làm những gì chúng tôi muốn, nhưng làm thế nào để chúng tôi thực sự đặt nó vào lớp của chúng tôi? Vâng, tài sản sử dụng trang trí; Tại sao chúng ta không thể? Chúng ta hãy xem nó có thể trông như thế nào (từ đây trở đi, tôi sẽ chỉ sử dụng một 'thuộc tính' duy nhất,

e.x[a,b,c] = 10
e.x[d,e,f] = 20
3).

class Example(object):

    @uberProperty
    def x(self):
        return defaultX()

Điều này không thực sự hoạt động, tất nhiên. Chúng tôi phải thực hiện

def x(self, *args):
    return defaultX()
3 và đảm bảo rằng nó xử lý cả hai và bộ.

Hãy bắt đầu với GetS.

Nỗ lực đầu tiên của tôi là chỉ cần tạo một đối tượng UberProperty mới và trả về nó:

def uberProperty(f):
    return UberProperty(f)

Tất nhiên, tôi đã nhanh chóng phát hiện ra rằng điều này không hoạt động: Python không bao giờ ràng buộc người được gọi với đối tượng và tôi cần đối tượng để gọi hàm. Ngay cả việc tạo ra người trang trí trong lớp cũng không hoạt động, vì mặc dù bây giờ chúng tôi có lớp học, chúng tôi vẫn không có đối tượng để làm việc.

Vì vậy, chúng tôi sẽ cần có khả năng làm nhiều hơn ở đây. Chúng tôi biết rằng một phương pháp chỉ cần được biểu diễn một lần, vì vậy hãy tiếp tục và giữ cho người trang trí của chúng tôi, nhưng sửa đổi

e.x = [a,b,c,10]
e.x = [d,e,f,30]
6 để chỉ lưu trữ tài liệu tham khảo
e.x = [a,b,c,10]
e.x = [d,e,f,30]
5:

class UberProperty(object):

    def __init__(self, method):
        self.method = method

Nó cũng không thể gọi được, vì vậy hiện tại không có gì hoạt động.

Làm thế nào để chúng ta hoàn thành bức tranh? Chà, chúng ta sẽ kết thúc như thế nào khi chúng ta tạo lớp ví dụ bằng cách sử dụng trình trang trí mới của chúng ta:

class Example(object):
    def __init__(self, x=None, y=None):
        self._x = x
        self._y = y

    @property
    def x(self):
        return self.x or self.defaultX()

    @x.setter
    def x(self, value):
        self._x = value

    @property
    def y(self):
        return self.y or self.defaultY()

    @y.setter
    def y(self, value):
        self._y = value

    # default{XY} as before.
0

Trong cả hai trường hợp, chúng tôi lấy lại

e.x = [a,b,c,10]
e.x = [d,e,f,30]
6, tất nhiên không thể gọi được, vì vậy điều này không được sử dụng nhiều.

Những gì chúng ta cần là một số cách để tự động ràng buộc thể hiện

e.x = [a,b,c,10]
e.x = [d,e,f,30]
6 được tạo bởi trình trang trí sau khi lớp được tạo thành một đối tượng của lớp trước khi đối tượng đó được trả lại cho người dùng đó để sử dụng. Ừm, vâng, đó là một cuộc gọi
e.x[a,b,c] = 10
e.x[d,e,f] = 20
1, anh bạn.

Chúng ta hãy viết những gì chúng ta muốn kết quả tìm thấy của chúng ta là đầu tiên. Chúng tôi ràng buộc một

e.x = [a,b,c,10]
e.x = [d,e,f,30]
6 với một ví dụ, vì vậy một điều rõ ràng để trở lại sẽ là một ràng buộc. Đây là nơi chúng tôi thực sự duy trì trạng thái cho thuộc tính
e.x[a,b,c] = 10
e.x[d,e,f] = 20
3.

class Example(object):
    def __init__(self, x=None, y=None):
        self._x = x
        self._y = y

    @property
    def x(self):
        return self.x or self.defaultX()

    @x.setter
    def x(self, value):
        self._x = value

    @property
    def y(self):
        return self.y or self.defaultY()

    @y.setter
    def y(self, value):
        self._y = value

    # default{XY} as before.
1

Bây giờ chúng tôi đại diện; Làm thế nào để có được những thứ này vào một đối tượng? Có một vài cách tiếp cận, nhưng cách dễ nhất để giải thích chỉ sử dụng phương pháp

e.x[a,b,c] = 10
e.x[d,e,f] = 20
1 để thực hiện ánh xạ đó. Vào thời điểm
e.x[a,b,c] = 10
e.x[d,e,f] = 20
1 được gọi là các nhà trang trí của chúng tôi đã chạy, vì vậy chỉ cần xem qua
print e.x     -> The default at time T0
e.x = 1
print e.x     -> 1
e.x = None
print e.x     -> The default at time T1
3 của đối tượng và cập nhật bất kỳ thuộc tính nào trong đó giá trị của thuộc tính thuộc loại
e.x = [a,b,c,10]
e.x = [d,e,f,30]
6.

Bây giờ, Uber-Properies rất tuyệt và có lẽ chúng tôi sẽ muốn sử dụng chúng rất nhiều, vì vậy thật hợp lý khi chỉ tạo một lớp cơ sở làm điều này cho tất cả các lớp con. Tôi nghĩ rằng bạn biết những gì lớp cơ sở sẽ được gọi.

class Example(object):
    def __init__(self, x=None, y=None):
        self._x = x
        self._y = y

    @property
    def x(self):
        return self.x or self.defaultX()

    @x.setter
    def x(self, value):
        self._x = value

    @property
    def y(self):
        return self.y or self.defaultY()

    @y.setter
    def y(self, value):
        self._y = value

    # default{XY} as before.
2

Chúng tôi thêm điều này, thay đổi ví dụ của chúng tôi thành kế thừa từ

print e.x     -> The default at time T0
e.x = 1
print e.x     -> 1
e.x = None
print e.x     -> The default at time T1
5 và ...

class Example(object):
    def __init__(self, x=None, y=None):
        self._x = x
        self._y = y

    @property
    def x(self):
        return self.x or self.defaultX()

    @x.setter
    def x(self, value):
        self._x = value

    @property
    def y(self):
        return self.y or self.defaultY()

    @y.setter
    def y(self, value):
        self._y = value

    # default{XY} as before.
3

Sau khi sửa đổi

e.x[a,b,c] = 10
e.x[d,e,f] = 20
3 là:

class Example(object):
    def __init__(self, x=None, y=None):
        self._x = x
        self._y = y

    @property
    def x(self):
        return self.x or self.defaultX()

    @x.setter
    def x(self, value):
        self._x = value

    @property
    def y(self):
        return self.y or self.defaultY()

    @y.setter
    def y(self, value):
        self._y = value

    # default{XY} as before.
4

Chúng ta có thể chạy một bài kiểm tra đơn giản:

class Example(object):
    def __init__(self, x=None, y=None):
        self._x = x
        self._y = y

    @property
    def x(self):
        return self.x or self.defaultX()

    @x.setter
    def x(self, value):
        self._x = value

    @property
    def y(self):
        return self.y or self.defaultY()

    @y.setter
    def y(self, value):
        self._y = value

    # default{XY} as before.
5

Và chúng tôi nhận được đầu ra mà chúng tôi muốn:

class Example(object):
    def __init__(self, x=None, y=None):
        self._x = x
        self._y = y

    @property
    def x(self):
        return self.x or self.defaultX()

    @x.setter
    def x(self, value):
        self._x = value

    @property
    def y(self):
        return self.y or self.defaultY()

    @y.setter
    def y(self, value):
        self._y = value

    # default{XY} as before.
6

(Gee, tôi đang làm việc muộn.)

Lưu ý rằng tôi đã sử dụng

print e.x     -> The default at time T0
e.x = 1
print e.x     -> 1
e.x = None
print e.x     -> The default at time T1
7,
print e.x     -> The default at time T0
e.x = 1
print e.x     -> 1
e.x = None
print e.x     -> The default at time T1
8 và
print e.x     -> The default at time T0
e.x = 1
print e.x     -> 1
e.x = None
print e.x     -> The default at time T1
9 ở đây. Điều này là do tôi chưa được liên kết trong các phương tiện để các loại này tự động được trả lại.

Nhưng tôi nghĩ rằng đây là một nơi tốt để dừng lại bây giờ, bởi vì tôi đang mệt mỏi. Bạn cũng có thể thấy rằng chức năng cốt lõi mà chúng tôi muốn được đặt ra; Phần còn lại là thay đồ cửa sổ. Mặc quần áo cửa sổ khả năng sử dụng quan trọng, nhưng điều đó có thể đợi cho đến khi tôi có một thay đổi để cập nhật bài đăng.

Tôi sẽ hoàn thành ví dụ trong bài đăng tiếp theo bằng cách giải quyết những điều này:

  • Chúng ta cần đảm bảo

    e.x[a,b,c] = 10
    e.x[d,e,f] = 20
    
    1 của UberObject luôn được gọi bởi các lớp con.

    • Vì vậy, chúng tôi buộc nó được gọi là ở đâu đó hoặc chúng tôi ngăn nó được thực hiện.
    • Chúng ta sẽ xem làm thế nào để làm điều này với một metaclass.
  • Chúng ta cần đảm bảo rằng chúng ta xử lý trường hợp phổ biến trong đó ai đó 'bí danh' là một chức năng khác với một thứ khác, chẳng hạn như:

    class Example(object):
        def __init__(self, x=None, y=None):
            self._x = x
            self._y = y
    
        @property
        def x(self):
            return self.x or self.defaultX()
    
        @x.setter
        def x(self, value):
            self._x = value
    
        @property
        def y(self):
            return self.y or self.defaultY()
    
        @y.setter
        def y(self, value):
            self._y = value
    
        # default{XY} as before.
    
    7
  • Chúng tôi cần

    class UberProperty(object):
    
        def __init__(self, method):
            self.method = method
            self.value = None
            self.isSet = False
    
        def setValue(self, value):
            self.value = value
            self.isSet = True
    
        def clearValue(self):
            self.value = None
            self.isSet = False
    
    1 để trả về
    class UberProperty(object):
    
        def __init__(self, method):
            self.method = method
            self.value = None
            self.isSet = False
    
        def setValue(self, value):
            self.value = value
            self.isSet = True
    
        def clearValue(self):
            self.value = None
            self.isSet = False
    
    2 theo mặc định.

    • Những gì chúng ta thực sự thấy là đây là một lĩnh vực mà mô hình thất bại.
    • Hóa ra chúng ta sẽ luôn cần sử dụng một cuộc gọi chức năng để có được giá trị.
    • Nhưng chúng ta có thể làm cho nó trông giống như một cuộc gọi chức năng thông thường và tránh phải sử dụng
      class UberProperty(object):
      
          def __init__(self, method):
              self.method = method
              self.value = None
              self.isSet = False
      
          def setValue(self, value):
              self.value = value
              self.isSet = True
      
          def clearValue(self):
              self.value = None
              self.isSet = False
      
      2. (Làm điều này là hiển nhiên, nếu bạn chưa từng sửa nó.)
  • Chúng tôi cần hỗ trợ cài đặt

    class UberProperty(object):
    
        def __init__(self, method):
            self.method = method
            self.value = None
            self.isSet = False
    
        def setValue(self, value):
            self.value = value
            self.isSet = True
    
        def clearValue(self):
            self.value = None
            self.isSet = False
    
    4, như trong
    class UberProperty(object):
    
        def __init__(self, method):
            self.method = method
            self.value = None
            self.isSet = False
    
        def setValue(self, value):
            self.value = value
            self.isSet = True
    
        def clearValue(self):
            self.value = None
            self.isSet = False
    
    5. Chúng tôi cũng có thể làm điều này trong lớp cha, nhưng chúng tôi sẽ cần cập nhật mã
    e.x[a,b,c] = 10
    e.x[d,e,f] = 20
    
    1 của mình để xử lý nó.

  • Cuối cùng, chúng tôi sẽ thêm các thuộc tính được tham số hóa. Nó cũng khá rõ ràng làm thế nào chúng ta cũng sẽ làm điều này.

Đây là mã tồn tại cho đến nay:

class Example(object):
    def __init__(self, x=None, y=None):
        self._x = x
        self._y = y

    @property
    def x(self):
        return self.x or self.defaultX()

    @x.setter
    def x(self, value):
        self._x = value

    @property
    def y(self):
        return self.y or self.defaultY()

    @y.setter
    def y(self, value):
        self._y = value

    # default{XY} as before.
8

[1] Tôi có thể bị tụt lại về việc liệu đây vẫn là trường hợp.

Tôi có nên sử dụng setters và getters trong Python không?

Getters và setters trong Python thường được sử dụng khi: chúng tôi sử dụng getters & setters để thêm logic xác thực xung quanh việc nhận và đặt giá trị. Để tránh truy cập trực tiếp của trường lớp, tức là các biến riêng tư không thể được truy cập trực tiếp hoặc sửa đổi bởi người dùng bên ngoài.to add validation logic around getting and setting a value. To avoid direct access of a class field i.e. private variables cannot be accessed directly or modified by external user.

Lợi ích của việc sử dụng thuộc tính thay vì chỉ có hàm setter là gì?

Ưu điểm của các thuộc tính là chúng giống hệt nhau về mặt cú pháp với quyền truy cập thuộc tính, vì vậy bạn có thể thay đổi từ cái này sang cái khác mà không có bất kỳ thay đổi nào đối với mã máy khách.syntactically identical to attribute access, so you can change from one to another without any changes to client code.

Là getters và setters thuộc tính?

Các thuộc tính của người phụ kiện được thể hiện bằng các phương thức của Get Getter và và Set Setter.Trong một đối tượng theo nghĩa đen, chúng được ký hiệu là get và set: hãy để obj = {get propname () {// getter, mã được thực thi khi nhận obj.propname}, đặt propname (value) {// setter, mã được thực thi trên cài đặt obj.. In an object literal they are denoted by get and set : let obj = { get propName() { // getter, the code executed on getting obj. propName }, set propName(value) { // setter, the code executed on setting obj.

Thuộc tính () trong Python là gì?

Hàm Python Property () trả về đối tượng của lớp thuộc tính và nó được sử dụng để tạo thuộc tính của một lớp.Cú pháp: Thuộc tính (FGET, FSET, FDEL, DOC) Tham số: fget () - Được sử dụng để nhận giá trị của thuộc tính.fset () - Được sử dụng để đặt giá trị của thuộc tính.returns the object of the property class and it is used to create property of a class. Syntax: property(fget, fset, fdel, doc) Parameters: fget() – used to get the value of attribute. fset() – used to set the value of attribute.