面向对象:classmethod、staticmethod、property
一、classmethod(类方法)、staticmethod(静态方法)
方法包括:普通方法、类方法和静态方法,三种方法在内存中都归属于类,区别在于调用方式不同。
# 普通方法 由对象调用,至少一个self参数,执行普通方法时,自动将调用该方法的对象赋值给self。 # 类方法 由类调用,至少一个cls参数,执行类方法时,自动将调用该方法的类赋值给cls;当一个方法的操作只涉及静态属性的时候,就应该使用classmethod来装饰这个方法。 # 静态方法 由类调用,无默认参数;在面向对象的程序中,如果一个函数既和对象没有关系,也和类没有关系,那么就用staticmethod将这个函数变成一个静态方法。
class Person(object): country = "美国" def __init__(self, name): self.name = name def func(self): """定义普通方法,至少有一个self参数""" print(self.name) @classmethod def change_country(cls): """定义类方法,至少有一个cls参数""" cls.country = "中国" @staticmethod def sleep(): """定义静态方法,无默认参数""" print("正在睡觉...") person = Person("pd") ########## 调用普通方法 ########## person.func() # pd ########## 调用类方法 ########## Person.change_country() # 可以通过类的名字调用类方法 # person.change_country() # 还可以通过这个类创建出来的对象去调用类方法 print(Person.country) # 中国 ########## 调用静态方法 ########## Person.sleep() # 通过类的名字去调用静态方法 person.sleep() # 通过实例对象去调用静态方法
相同点:对于所有的方法而言,均属于类(非对象)中;所以,在内存中也只保存一份。
不同点:方法调用者不同,调用方法时自动传入的参数不同。
二、property(属性)
属性非常简单,因为Python中的属性其实是普通方法的变种。
2.1 属性的基本使用
class Person(object): def __init__(self, name): self.name = name def func1(self): print(self.name) @property # 定义属性 def func2(self): print(self.name) obj = Person("pd") obj.func1() # pd obj.func2 # 调用属性,结果同上
由属性的定义和调用要注意以下几点:
定义时,在普通方法的基础上添加 @property 装饰器
定义时,属性仅有一个self参数
调用时,无需括号
方法:obj.func1()
属性:obj.func2
属性存在意义是:访问属性时可以制造出和访问字段完全相同的假象。
属性由方法变种而来,如果Python中没有属性,方法完全可以代替其功能。
2.2 属性的两种定义方式
装饰器方式:在类的普通方法上应用装饰器
Python中的类有经典类和新式类,新式类的属性比经典类的属性丰富。(如果类继object,那么该类是新式类)
经典类,只有一种@property装饰器(如上面的示例那样)
新式类,具有三种@property装饰器
class Goods(object): def __init__(self): self.__price = 100 # 设定原价 self.discount = 0.8 # 设定折扣 @property def price(self): """获取价格""" return self.__price * self.discount @price.setter def price(self, value): """设置价格""" self.__price = value @price.deleter def price(self): """删除商品原价这个属性""" del self.__price obj = Goods() print(obj.price) # 自动执行 @property 修饰的 price 方法,并获取方法的返回值 obj.price = 200 # 自动执行 @price.setter 修饰的 price 方法,并将200赋值给方法的参数 print(obj.price) del obj.price # 自动执行 @price.deleter 修饰的 price 方法,删除商品原价这个属性 print(obj.price) # 报错,AttributeError: 'Goods' object has no attribute '_Goods__price'
静态字段方式:在类中定义值为property对象的静态字段
当使用静态字段的方式创建属性时,经典类和新式类无区别。
示例:
class Foo(object): def get_name(self): return "pd" NAME = property(get_name) obj = Foo() ret = obj.NAME # 自动调用func方法,并获取方法的返回值 print(ret) # pd
property的构造方法中有个四个参数:
第一个参数是方法名,调用 对象.属性 时自动触发执行方法 第二个参数是方法名,调用 对象.属性 = XXX 时自动触发执行方法 第三个参数是方法名,调用 del 对象.属性 时自动触发执行方法 第四个参数是字符串,调用 对象.属性.__doc__,此参数是该属性的描述信息
class Goods(object): def __init__(self): self.__price = 100 # 设定原价 self.discount = 0.8 # 设定折扣 def get_price(self): """获取价格""" return self.__price * self.discount def set_price(self, value): """设置价格""" self.__price = value def del_price(self): """删除商品原价这个属性""" del self.__price PRICE = property(get_price, set_price, del_price, "价格属性描述...") obj = Goods() ret = obj.PRICE # 自动调用第一个参数中定义的方法:get_name print(ret) # 80.0 obj.PRICE = 200 # 自动调用第二个参数中定义的方法:set_name,并将"200"当作参数传入 print(obj.PRICE ) # 160.0 del obj.PRICE # 自动调用第三个参数中定义的方法:del_name
注意:Python 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) ############### Look Here ############### def _get_post(self): if not hasattr(self, '_post'): self._load_post_and_files() return self._post ############### Look Here ############### 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 ############### Look Here ############### POST = property(_get_post, _set_post) FILES = property(_get_files) REQUEST = property(_get_request)