美文网首页Python知识锦集动态语言Ruby Python
Python高级语法4:类对象和实例对象访问属性的区别和prop

Python高级语法4:类对象和实例对象访问属性的区别和prop

作者: IIronMan | 来源:发表于2018-12-15 23:54 被阅读3次

    一、再论静态方法和类方法以及实例方法

    • 1.1、类属性、实例属性
      它们在定义和使用中有所区别,而最本质的区别内存中保存的位置不同

      • 实例属性属于对象

      • 类属性属于类

        class Province(object):
              # 类属性
              country = '中国'
        
              def __init__(self, name):
                    # 实例属性
                    self.name = name
        
        
        # 创建一个实例对象
        obj = Province('山东省')
        # 直接访问实例属性
        print(obj.name)
        # 直接访问类属性
        Province.country
        

        由上述代码可以看出【实例属性需要通过对象来访问】【类属性通过类访问】,在使用上可以看出 实例属性和类属性的归属是不同的

        • 类属性在内存中只保存一份
        • 实例属性在每个对象中都要保存一份
      • 应用场景:通过类创建实例对象时,如果每个对象需要具有相同名字的属性,那么就使用类属性,用一份既可

    • 1.2、实例方法、静态方法和类方法 (类里面的三种方法,区别在于调用方式不同)

      • 实例方法:由对象调用;至少一个self参数;执行实例方法时,自动将调用该方法的对象赋值给self;

      • 类方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类赋值给cls;

      • 静态方法:由类调用;无默认参数;

        class Foo(object):
             def __init__(self, name):
                   self.name = name
        
        def ord_func(self):
             """ 定义实例方法,至少有一个self参数 """
             # print(self.name)
             print('实例方法')
        
        @classmethod
        def class_func(cls):
             """ 定义类方法,至少有一个cls参数 """
             print('类方法')
        
        @staticmethod
        def static_func():
             """ 定义静态方法 ,无默认参数"""
             print('静态方法')
        
        
        f = Foo("中国")
        # 调用实例方法
        f.ord_func()
        
        # 调用类方法
        Foo.class_func()
        
        # 调用静态方法
        Foo.static_func()
        
        • 相同点:对于所有的方法而言,均属于类,所以 在内存中也只保存一份
        • 不同点:方法调用者不同、调用方法时自动传入的参数不同。

    二、property 属性

    • 2.1、什么是property属性,在方法名的加上property就是 property属性,在调用上 dog.run 优于 dog.eat()

      class Dog(object):
      
            def eat(self):
                  print("吃")
      
            # 定义property属性
            @property
            def run(self):
                  print("跑")
      
      dog = Dog()
      dog.eat()  # 调用实例方法
      dog.run    # 调用property属性
      
      • property属性的定义和调用要注意一下几点:
        • 定义时,在实例方法的基础上添加 @property 装饰器;并且仅有一个self参数

        • 调用时,无需括号

          方法:dog.eat() 
          property属性:dog.run
          
    • 2.2、property属性有两种方式

      • 装饰器 即:在方法上应用装饰器

      • 类属性 即:在类中定义值为property对象的类属性

      • 2.2.1、装饰器方式
        在类的实例方法上应用@property装饰器,Python中的类有经典类和新式类,新式类的属性比经典类的属性丰富。( 如果类继object,那么该类是新式类

        • 经典类,具有一种@property装饰器

          class Goods:
             @property
             def price(self):
                  return "laowang"
          # ############### 调用 ###############
          obj = Goods()
          result = obj.price  # 自动执行 @property 修饰的 price 方法,并获取方法的返回值
          print(result)
          
        • 新式类,具有三种@property装饰器

          # ############### 定义 ###############
          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 方法
          

          注意

          • 经典类中的属性只有一种访问方式,其对应被 @property 修饰的方法
          • 新式类中的属性有三种访问方式,并分别对应了三个被@property、@方法名.setter、@方法名.deleter修饰的方法

          由于新式类中具有三种访问方式,我们可以根据它们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除

          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     # 删除商品原价
          
      • 2.2.2、类属性 方式,创建值为property对象的类属性,我比较喜欢这个方式

        • 当使用 类属性 的方式创建 property属性 时,经典类新式类 无区别

          class Person:
             def get_name(self):
                return 'laowang'
          
             BAR = property(get_name)
          
          obj = Person()
          reuslt = obj.BAR  # 自动调用get_bar方法,并获取方法的返回值
          print(reuslt)
          
        • property 方法中有个 四个参数

          • 第 1 个参数是方法名,调用 对象.属性 时自动触发执行方法
          • 第 2 个参数是方法名,调用 对象.属性 = XXX 时自动触发执行方法
          • 第 3 个参数是方法名,调用 del 对象.属性 时自动触发执行方法
          • 第 4 个参数是字符串,调用 对象.属性.__doc__ ,此参数是该属性的描述信息
        class Person(object):
        
             def __init__(self,name):
                  self.name = name
        
             def get_name(self): 
                  return self.name
        
             def set_name(self,new_name):
        
                  self.name = new_name
                  print("设置名字为:%s"%self.name)
        
             def del_name(self):
                  del  self.name
        
             BAR = property(get_name,set_name,del_name,"描述信息...")
        
        
        person = Person("小王")
        person.BAR      # 自动调用第一个参数中定义的方法:get_name
        person.BAR = "小李" # 自动调用第二个参数中定义的方法:set_name方法,并将 "小李" 当作参数传入
        
        person.del_name  # 自动调用第三个参数中定义的方法:del_name方法
        
        desc = Person.BAR.__doc__   # 自动获取第四个参数中设置的值:描述信息...
        
        print("描述是:%s"%desc)
        

        由于类属性方式创建 property属性 具有3种访问方式,我们可以根据它们几个属性的访问特点,分别将 三个方法定义为对同一个属性:获取、修改、删除,如下商品的价格

        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     # 删除商品原价
        
    • 2.3、Django框架中应用了property属性(了解)
      WEB框架 Django 的视图中 request.POST 就是使用的类属性的方式创建的属性

      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)
      

    综上所述

    • 定义property属性共有两种方式,分别是【装饰器】和【类属性】,而【装饰器】方式针对经典类和新式类又有所不同。
    • 通过使用property属性,能够简化调用者在获取数据的流程

    相关文章

      网友评论

      本文标题:Python高级语法4:类对象和实例对象访问属性的区别和prop

      本文链接:https://www.haomeiwen.com/subject/taoahqtx.html