Python advanced syntax _property attribute explanation and application

Explanation and application of property attributes

property attribute

What is the property attribute?

  • A special attribute that is used as an instance attribute, which can correspond to a method
# ############### 定义 ###############
class Foo:
    def func(self):
        pass

    # 定义property属性
    @property
    def prop(self):
        pass

# ############### 调用 ###############
foo_obj = Foo()
foo_obj.func()  # 调用实例方法
foo_obj.prop  # 调用property属性

property

The definition and call of the property attribute should pay attention to the following points:

  • When defining, add @property decorator to the instance method; and there is only one self parameter
  • When calling, no parentheses are required
方法:foo_obj.func()
property属性:foo_obj.prop

Simple example

Take an online shopping mall as an example. For a list page that displays products in the shopping mall, it is impossible to display all the content in the database on the page every time you request it, but to display it partially through the paging function, so when requesting data from the database The function of specifying to get all the data from the mth to the nth page to be displayed includes:

  • Calculate m and n according to the current page requested by the user and the total number of data

  • Request data in the database according to m and n

代码示例:

# ############### 定义 ###############
class Pager:
    def __init__(self, current_page):
        # 用户当前请求的页码(第一页、第二页...)
        self.current_page = current_page
        # 每页默认显示10条数据
        self.per_items = 10 

    @property
    def start(self):
        val = (self.current_page - 1) * self.per_items
        return val

    @property
    def end(self):
        val = self.current_page * self.per_items
        return val

# ############### 调用 ###############
p = Pager(1)
p.start  # 就是起始值,即:m
p.end  # 就是结束值,即:n


It can be seen from the above

  • The function of Python's property attribute is: a series of logical calculations are performed inside the property attribute, and the calculation result is finally returned.

There are two ways of property attributes

  • Decorator is: apply decorator on method

  • The class attribute is: the class attribute whose value is the property object is defined in the class

Decorator way

Apply the @property decorator on the instance method of the class

There are classic and new-style classes in Python classes. The new-style classes have more attributes than the classic classes.

Here is a brief introduction to what is 经典类, what is 新式类, and the difference between the two.

In Python 2.x, the default classes are classic classes, and only those explicitly inherited from object are new-style classes, namely:

class Person(object):
	pass  # 新式类写法


class Person():
	pass  # 经典类写法


class Person:
	pass  # 经典类写法

But in Python 3.x, the classic classes are cancelled. By default, they are all new-style classes, and there is no need to explicitly inherit object, that is to say

class Person(object):
	pass


class Person():
	pass


class Person:
	pass

There is no difference between these three ways of writing, but the first one is recommended.

The most obvious difference between them is that the order of inheritance search has changed, namely

经典类Multiple inheritance 搜索顺序(深度优先):
first goes to the left side of the inheritance tree to find, and then returns to start to find the right side.

新式类Multiple inheritance 搜索顺序(广度优先):
searches in the horizontal direction first and then upwards.

For specific details, please refer to the previous Python basics: the explanation of inheritance-depth first and breadth first in the three object-oriented features .

Classic class, with a @property decorator

# ############### 定义 ###############    
class Goods:
    @property
    def price(self):
        return "laowang"
# ############### 调用 ###############
obj = Goods()
result = obj.price  # 自动执行 @property 修饰的 price 方法,并获取方法的返回值
print(result)


New-style class with three @property decorators

# ############### 定义 ###############
class Goods:
    """python3中默认继承object类
        以python2、3执行此程序的结果不同,因为只有在python3中才有@xxx.setter  @xxx.deleter
    """
    @property
    def price(self):
        print('@property')

    @price.setter
    def price(self, value):
        print('@price.setter')

    @price.deleter
    def price(self):
        print('@price.deleter')

# ############### 调用 ###############
obj = Goods()
obj.price          # 自动执行 @property 修饰的 price 方法,并获取方法的返回值
obj.price = 123    # 自动执行 @price.setter 修饰的 price 方法,并将  123 赋值给方法的参数
del obj.price      # 自动执行 @price.deleter 修饰的 price 方法

注意

  • The property in the classic class has only one access method, which corresponds to the method modified by @property
  • There are three ways to access the properties in the new-style class, and they correspond to three methods modified by @property, @methodname.setter, and @methodname.deleter respectively.

Since there are three access methods in the new-style class, we can define the three methods as the same attribute: get, modify, and delete according to the access characteristics of their several attributes.

比如以获取商品折扣价格为例子,参考下方示例Demo

class Goods(object):

    def __init__(self):
        # 原价
        self.original_price = 100
        # 折扣
        self.discount = 0.8

    @property
    def price(self):
        # 实际价格 = 原价 * 折扣
        new_price = self.original_price * self.discount
        return new_price

    @price.setter
    def price(self, value):
        self.original_price = value

    @price.deleter
    def price(self):
        del self.original_price

obj = Goods()
obj.price         # 获取商品价格
obj.price = 200   # 修改商品原价
del obj.price     # 删除商品原价

Class attribute mode: create a class attribute whose value is a property object

  • When you create a property attribute using the class attribute of the way, 经典类and 新式类no difference

Code example:

class Foo:
    def get_bar(self):
        return 'laowang'

    BAR = property(get_bar)

obj = Foo()
reuslt = obj.BAR  # 自动调用get_bar方法,并获取方法的返回值
print(reuslt)

There are four parameters in the property method

  • The first parameter is the name of the method, which automatically triggers the execution of the method when the object. property is called
  • The second parameter is the name of the method, calling the object. Property = XXX automatically trigger the execution of the method
  • The third parameter is the name of the method, which automatically triggers the execution of the method when the del object is called. The attribute
  • The fourth parameter is a string, calling object.attribute.doc , this parameter is the description of the attribute

Code example:

class Foo(object):
    def get_bar(self):
        print("getter...")
        return 'laowang'

    def set_bar(self, value): 
        """必须两个参数"""
        print("setter...")
        return 'set value' + value

    def del_bar(self):
        print("deleter...")
        return 'laowang'

    BAR = property(get_bar, set_bar, del_bar, "description...")

obj = Foo()

obj.BAR  # 自动调用第一个参数中定义的方法:get_bar
obj.BAR = "alex"  # 自动调用第二个参数中定义的方法:set_bar方法,并将“alex”当作参数传入
desc = Foo.BAR.__doc__  # 自动获取第四个参数中设置的值:description...
print(desc)
del obj.BAR  # 自动调用第三个参数中定义的方法:del_bar方法

Since the creation of the property attribute by the class attribute method has three access methods, we can define the three methods as the same attribute according to the access characteristics of several attributes: get, modify, delete

同样以获取商品折扣价格为例子,参考下方示例Demo

class Goods(object):

    def __init__(self):
        # 原价
        self.original_price = 100
        # 折扣
        self.discount = 0.8

    def get_price(self):
        # 实际价格 = 原价 * 折扣
        new_price = self.original_price * self.discount
        return new_price

    def set_price(self, value):
        self.original_price = value

    def del_price(self):
        del self.original_price

    PRICE = property(get_price, set_price, del_price, '价格属性描述...')

obj = Goods()
obj.PRICE         # 获取商品价格
obj.PRICE = 200   # 修改商品原价
del obj.PRICE     # 删除商品原价

The property attribute is applied in the Django framework (currently only for understanding)

The request.POST in the view of the WEB framework Django is the attribute created by the way of using the class attribute

class WSGIRequest(http.HttpRequest):
    def __init__(self, environ):
        script_name = get_script_name(environ)
        path_info = get_path_info(environ)
        if not path_info:
            # Sometimes PATH_INFO exists, but is empty (e.g. accessing
            # the SCRIPT_NAME URL without a trailing slash). We really need to
            # operate as if they'd requested '/'. Not amazingly nice to force
            # the path like this, but should be harmless.
            path_info = '/'
        self.environ = environ
        self.path_info = path_info
        self.path = '%s/%s' % (script_name.rstrip('/'), path_info.lstrip('/'))
        self.META = environ
        self.META['PATH_INFO'] = path_info
        self.META['SCRIPT_NAME'] = script_name
        self.method = environ['REQUEST_METHOD'].upper()
        _, content_params = cgi.parse_header(environ.get('CONTENT_TYPE', ''))
        if 'charset' in content_params:
            try:
                codecs.lookup(content_params['charset'])
            except LookupError:
                pass
            else:
                self.encoding = content_params['charset']
        self._post_parse_error = False
        try:
            content_length = int(environ.get('CONTENT_LENGTH'))
        except (ValueError, TypeError):
            content_length = 0
        self._stream = LimitedStream(self.environ['wsgi.input'], content_length)
        self._read_started = False
        self.resolver_match = None

    def _get_scheme(self):
        return self.environ.get('wsgi.url_scheme')

    def _get_request(self):
        warnings.warn('`request.REQUEST` is deprecated, use `request.GET` or '
                      '`request.POST` instead.', RemovedInDjango19Warning, 2)
        if not hasattr(self, '_request'):
            self._request = datastructures.MergeDict(self.POST, self.GET)
        return self._request

    @cached_property
    def GET(self):
        # The WSGI spec says 'QUERY_STRING' may be absent.
        raw_query_string = get_bytes_from_wsgi(self.environ, 'QUERY_STRING', '')
        return http.QueryDict(raw_query_string, encoding=self._encoding)

    # ############### 看这里看这里  ###############
    def _get_post(self):
        if not hasattr(self, '_post'):
            self._load_post_and_files()
        return self._post

    # ############### 看这里看这里  ###############
    def _set_post(self, post):
        self._post = post

    @cached_property
    def COOKIES(self):
        raw_cookie = get_str_from_wsgi(self.environ, 'HTTP_COOKIE', '')
        return http.parse_cookie(raw_cookie)

    def _get_files(self):
        if not hasattr(self, '_files'):
            self._load_post_and_files()
        return self._files

    # ############### 看这里看这里  ###############
    POST = property(_get_post, _set_post)

    FILES = property(_get_files)
    REQUEST = property(_get_request)


In summary:

  • There are two ways to define property attributes, namely [decorator] and [class attribute], and the [decorator] method is different for classic and new-style classes.
  • By using the property attribute, you can simplify the caller's process of obtaining data.

property attribute-application

Private properties add getter and setter methods

Code example:

class Money(object):
    def __init__(self):
        self.__money = 0

    def getMoney(self):
        return self.__money

    def setMoney(self, value):
        if isinstance(value, int):
            self.__money = value
        else:
            print("error:不是整型数字")


Use property to upgrade getter and setter methods

Code example:

class Money(object):
    def __init__(self):
        self.__money = 0

    def getMoney(self):
        return self.__money

    def setMoney(self, value):
        if isinstance(value, int):
            self.__money = value
        else:
            print("error:不是整型数字")

    # 定义一个属性,当对这个money设置值时调用setMoney,当获取值时调用getMoney
    money = property(getMoney, setMoney)  

a = Money()
a.money = 100  # 调用setMoney方法
print(a.money)  # 调用getMoney方法

Use property instead of getter and setter methods

  • Re-implement the setting and reading method of an attribute, which can be used for boundary judgment
class Money(object):
    def __init__(self):
        self.__money = 0

    # 使用装饰器对money进行装饰,那么会自动添加一个叫money的属性,当调用获取money的值时,调用装饰的方法
    @property
    def money(self):
        return self.__money

    # 使用装饰器对money进行装饰,当对money设置值时,调用装饰的方法
    @money.setter
    def money(self, value):
        if isinstance(value, int):
            self.__money = value
        else:
            print("error:不是整型数字")

a = Money()
a.money = 100
print(a.money)

Guess you like

Origin blog.csdn.net/weixin_42250835/article/details/89977913