Khai báo @property trong python
Python
43
White

studybot viết ngày 21/06/2016

Dạo này có thời gian rảnh sau khi dự án dài hơi vừa kết thúc nên mình tranh thủ đọc mã nguồn của flask. Trong flask https://github.com/pallets/flask/blob/master/flask/app.py#L606 có đoạn code sau đây:

    @property
    def logger(self):
        """A :class:`logging.Logger` object for this application.  The
        default configuration is to log to stderr if the application is
        in debug mode.  This logger can be used to (surprise) log messages.
        Here some examples::

            app.logger.debug('A value for debugging')
            app.logger.warning('A warning occurred (%d apples)', 42)
            app.logger.error('An error occurred')

        .. versionadded:: 0.3
        """
        if self._logger and self._logger.name == self.logger_name:
            return self._logger
        with _logger_lock:
            if self._logger and self._logger.name == self.logger_name:
                return self._logger
            from flask.logging import create_logger
            self._logger = rv = create_logger(self)
            return rv

Tại sao tác giả lại khai báo @property và thuộc tính này có ý nghĩa gì

Descriptor

Nhớ lại là trước đây anh kiennt có một bài viết rất hay giải thích rất rõ ràng về python descriptor. Bạn nào muốn tìm hiểu kỹ hơn về descriptor trong python thì mình nghĩ nên đọc bài viết trên.

Ở đây rõ ràng tác giả đang dùng descriptor idiom. Đoạn mã trên có thể được hiểu như sau:

def logger(self): ...
logger = property(logger)

logger sẽ được gói trong hàm property. Hàm property thực chất định nghĩa các hàm gettersetter (nghe rất giống mùi Java) để lấy giá trị hoặc cài đặt giá trị biến. logger ở dòng thứ 2 sẽ trả về một đối tượng có thuộc tính __get____set__ (và cả __delete__). Khi bạn gọi app.logger thì thực chất hàm logger sẽ không được gọi mà logger ở dòng thứ 2 được gọi. Đối tượng này vì là một descriptor nên nó sẽ gọi __get__ và trả về giá trị logger thật sự cho bạn.

Nói cách khác: app.logger sẽ không phải là hàm logger (app.__dict__['logger']) ở dòng thứ nhất mà sẽ là type(app).__dict__['logger'].__get__(logger, type(app)) (type(app) là kiểu của biến app). Bằng việc khai báo @property ta thay đổi cách hàm logger được truy cập (cụ thể ở đây là thêm 1 tầng gián tiếp).

Vậy tại sao lại phải khai báo @property?

Khi gói một hàm trong @property thì @property chỉ định nghĩa getter còn không định nghĩa setter. Điều đó là bạn chỉ có thể gọi hàm đấy để lấy giá trị chứ không thể cài một giá trị khác cho hàm này. Nói cách khác ta có thể định nghĩa một biến readonly bằng @property. Điều này có thể được thấy rõ qua ví dụ sau:

$ python
Python 3.5.1 (default, May  9 2016, 18:24:40)
[GCC 4.2.1 Compatible Apple LLVM 7.3.0 (clang-703.0.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> class A:
...     @property
...     def name(self):
...         return self.__name
...     def __init__(self, name):
...         self.__name = name
...
>>>
>>> a = A('hello world')
>>> a.name
'hello world'
>>> a.name = 'test'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute

Biến name chỉ có thể truy cập được mà không thể cài được giá trị mới. Nói cách khác ta có một biến readonly tên là name.

Như vậy dụng ý của người viết hàm logger của flask là muốn hàm này chỉ là readonly và không muốn người khác thay đổi. Nói cách khác người viết đang cố gắng giới hạn thay đổi đến app.logger.

Lý do thì theo tớ đoán chắc có 2 ý:

  • logger tên quá tổng quát nên sợ người khác ghi đè
  • Python là ngôn ngữ rất dễ ghì đè biến này và người viết muốn hạn chế trường hợp đó.
Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

studybot

15 bài viết.
9 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
14 12
Một vài thủ thuật vim mới biết, ghi lại cho khỏi quên Căn lề các cột dữ liệu Để căn lề ấn phím esc (để sang command mode) :%column t Chọn th...
studybot viết hơn 3 năm trước
14 12
White
11 9
Javascript context (ngữ cảnh?) là một khái niệm khó hiểu trong Javascript. Tuy vậy để code Javascript thành thạo thì việc nắm vững context là điểu ...
studybot viết hơn 3 năm trước
11 9
White
9 1
Một vài ghi chép về Java Hotspot GC Garbage collector thực hiện việc quản lý bộ nhớ thông qua 3 công việc: Gán đối tượng vào pool các đối tượng m...
studybot viết hơn 3 năm trước
9 1
Bài viết liên quan
White
1 0
Mở đầu Như đã nói ở bài trước, mình đang nghiên cứu về Spark nên cần log lại một số thứ để dành sau này dùng đến :smile: Đối tượng hướng đến vẫn ...
Phạm Quốc Thắng viết hơn 2 năm trước
1 0
White
5 3
Observer pattern (python example) 1. Observer là gì : Theo như (Link) Observer Pattern là : A software design pattern in which an object, calle...
Khôi Trọng Nguyễn viết 2 năm trước
5 3
White
0 0
Web Framework Flask định nghĩa route bằng annotations kiểu như @route('/users/add', methods='GET']) def user_add(): pass Lợi thế của cách là...
studybot viết 3 năm trước
0 0
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
15 bài viết.
9 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!