本节主要内容:
1.面向对象中的成员
2.成员修饰符
3.特殊成员
4.异常处理
5.设计模式之单例模式
一.面向对象中的成员(类的成员)
类的成员总共可以分为3大类,每类中有不同的分支。
1.总述,基本分类
如下图所示: 类成员包括字段,方法,和属性
2.字段
如上图字段分为普通字段和静态字段,两者的使用有区别,但是最大的区别在于两者在内存中的保存位置有区别。
普通字段属于对象而静态字段属于类,在使用过程中谁的字段就由谁来调用。
静态字段和普通字段的定义如下:
在调用时分各自调用
#####类中的对象 字段 class Province: country = "中国" def __init__(self,name): self.name = name def show(self): print(self.name) hn = Province("hhh") #### hn.show() #一般情况下 自己访问自己的字段成员 对象访问自己的字段,类访问自己的字段 print(Province.country) ---直接用类调用其的静态字段 print(hn.name) --用对象来调用普通字段 ##!!!! 特殊情况下(python),也能对象去访问。 print(hn.country) --不建议使用 ##总之 谁的字段就用谁本身来访问。
通过上面的例子,得知【普通字段需要通过对象来访问】【静态字段通过类访问】他的应用场景,通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段
如下图:静态字段在内存中保留一份,普通字段在每个对象中都会保留,所以当有多个对象都使用的字段就设置成为静态字段。
3.方法
方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。
- 普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self;
- 类方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类复制给cls;
- 静态方法:由类调用;无默认参数; --(相当于普通的函数,为了可维护性和代码的可读性写到类中称为类方法)
class Foo: 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()
相同点: 对于所有的方法而言都属于类,所以在内存中也保留一份
不同点:调用者不同(对象调用&类直接调用),调用时自动传入的参数不同(self,cls,无)
4. 属性
Python中的属性其实是普通方法的变种,必须掌握他的定义和调用方式
# ############### 定义 ############### class Foo: def func(self): pass # 定义属性 @property #--关键标识-- def prop(self): pass # ############### 调用 ############### foo_obj = Foo() foo_obj.func() foo_obj.prop #调用属性
由属性的定义和调用要注意一下几点:
- 定义时,在普通方法的基础上添加 @property 装饰器;
- 定义时,属性仅有一个self参数
- 调用时,无需括号
调用方法:foo_obj.func()
调用属性:foo_obj.prop
访问属性时可以制造出和访问字段完全相同的假象,如果Python中没有属性,方法完全可以代替其功能。
定义属相的方法:
- 装饰器 即:在方法上应用装饰器
- 静态字段 即:在类中定义值为property对象的静态字段
装饰器方式:在类的普通方法上应用@property装饰器,在python的经典类和新式类中,新式类的属性比经典类的属性丰富
经典类,具有一种@property装饰器(如上一步实例)
# ############### 定义 ############### class Goods: @property def price(self): return "wupeiqi" # ############### 调用 ############### obj = Goods() result = obj.price # 自动执行 @property 修饰的 price 方法,并获取方法的返回值
新式类,具有三种@property装饰器
# ############### 定义 ############### class Goods(object): @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对象的静态字段
当使用静态字段的方式创建属性时,经典类和新式类无区别
class Foo: def get_bar(self): return ‘wupeiqi‘ BAR = property(get_bar) obj = Foo() reuslt = obj.BAR # 自动调用get_bar方法,并获取方法的返回值 print reuslt
property的构造方法中有个四个参数
- 第一个参数是方法名,调用
对象.属性
时自动触发执行方法 - 第二个参数是方法名,调用
对象.属性 = XXX
时自动触发执行方法 - 第三个参数是方法名,调用
del 对象.属性
时自动触发执行方法 - 第四个参数是字符串,调用
对象.属性.__doc__
,此参数是该属性的描述信息
由于静态字段方式创建属性具有三种访问方式,我们可以根据他们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除
class Foo: def get_bar(self): return ‘wupeiqi‘ # *必须两个参数 def set_bar(self, value): return return ‘set value‘ + value def del_bar(self): return ‘wupeiqi‘ BAR = property(get_bar, set_bar, del_bar, ‘description...‘) obj = Foo() obj.BAR # 自动调用第一个参数中定义的方法:get_bar obj.BAR = "alex" # 自动调用第二个参数中定义的方法:set_bar方法,并将“alex”当作参数传入 del Foo.BAR # 自动调用第三个参数中定义的方法:del_bar方法 obj.BAE.__doc__ # 自动获取第四个参数中设置的值:description...
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) # ############### 看这里看这里 ############### 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) Django源码
源码
所以,定义属性共有两种方式,分别是【装饰器】和【静态字段】,而【装饰器】方式针对经典类和新式类又有所不同。
二.成员修饰符
私有成员和公有成员的定义不同:私有成员命名时,前两个字符是下划线。(特殊成员除外,例如:__init__、__call__、__dict__等)
class C: def __init__(self): self.name = ‘公有字段‘ self.__foo = "私有字段"
私有成员和公有成员的访问限制不同
静态字段
- 公有静态字段:类可以访问;类内部可以访问;派生类中可以访问
- 私有静态字段:仅类内部可以访问;
普通字段
- 公有普通字段:对象可以访问;类内部可以访问;派生类中可以访问
- 私有普通字段:仅类内部可以访
例子如下:
#成员修饰符 #公有的 私有的 1.公有的外部可以访问 class Foo(): def __init__(self,name): self.name = name def f1(self): print(self.name) obj = Foo("zhaowenche") print(obj.name) ##外部访问公有的字段是可以访问的 obj.f1() ##2.私有的外部不能访问内部可以访问 class Foo(): __cc = "this is cc" def __init__(self,name): self.__name = name def f1(self): print(self.__name) def f2(self): print(Foo.__cc) obj = Foo("zhaowenche") #print(obj.name) ##外部访问私有字段是报错的 obj.f1() obj.f2() #3.私有方法,外部不可以调用(即使他的子类,派生类等也是不可以调用的) class Foo(): __cc = "this is cc" def __init__(self,name): self.__name = name def __f1(self): print(self.__name) def f2(self): print(Foo.__cc) def f3(self): Foo.__f1(self) obj = Foo("zhaowenche") #print(obj.name) ##外部调用私有方法是有问题的 #obj.f1() obj.f2() obj.f3() ##通过f3的内部的调用__f1()是可以得到值的
三.类的特殊成员
1.__call__,
2.__str__
对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print ‘__call__‘ obj = Foo() # 执行 __init__ obj() # 执行 __call__
3.__dict__类或对象中的所有成员
class Province: country = ‘China‘ def __init__(self, name, count): self.name = name self.count = count def func(self, *args, **kwargs): print ‘func‘ # 获取类的成员,即:静态字段、方法、 print Province.__dict__ # 输出:{‘country‘: ‘China‘, ‘__module__‘: ‘__main__‘, ‘func‘: <function func at 0x10be30f50>, ‘__init__‘: <function __init__ at 0x10be30ed8>, ‘__doc__‘: None} obj1 = Province(‘HeBei‘,10000) print obj1.__dict__ # 获取 对象obj1 的成员 # 输出:{‘count‘: 10000, ‘name‘: ‘HeBei‘} obj2 = Province(‘HeNan‘, 3888) print obj2.__dict__ # 获取 对象obj1 的成员 # 输出:{‘count‘: 3888, ‘name‘: ‘HeNan‘}
4.__getitem__、__setitem__、__delitem__
用于索引操作,如字典。以上分别表示获取、设置、删除数据
#item class Foo: def __init__(self,name): self.name = name def __call__(self, *args, **kwargs): print("this is call ---") def __str__(self): print("this obj is {}".format(self.name)) return "this obj is {}".format(self.name) def __getitem__(self, item): print("getitem") def __setitem__(self, key, value): print("setitem") print(key,value) def __delitem__(self, key): print(key) obj = Foo("zhaowencheng") obj["aa"] #直接执行类中的getitem obj["bb"] = "123" #直接执行类中的seritem del obj["cc"] ##直接执行类中的delitem
################################## #访问 ######分片的方式访问 class Foo: def __init__(self,name): self.name = name def __getitem__(self, item): print(type(item)) print(item.start) print(item.stop) print(item.step) #print("getitem") def __setitem__(self, key, value): #print("setitem") print(type(key),type(value)) #print(key,value) def __delitem__(self, key): print(type(key)) def __iter__(self): yield 1 yield 2 yield 3 obj = Foo("zhaowencheng") obj[1:2] #直接执行类中的getitem obj[1:2] = [123,33,4,56,56,7] #直接执行类中的seritem del obj[1:2] ##直接执行类中的delitem for i in obj: print(i)
5.super执行其父类的方法
class C1: def f1(self): print("c1.f1") class C2(C1): def f1(self): super(C2,self).f1() print("c2.f1") obj = C2() #obj.f1() #直接打印除c2.f1 obj.f1() #当super时
应用:在源码不变的基础上通过自己新建类然后继承python中原有的字典类来实现有序字典
##########################实现有序字典 class MyDict(dict): def __init__(self): self.li = [] super(MyDict,self).__init__() def __setitem__(self, key, value): self.li.append(key) super(MyDict,self).__setitem__(key,value) def __str__(self): tem_list = [] for key in self.li: value = self.get(key) tem_list.append("‘%s‘:%s"%(key,value,)) temp_str = "{" + ",".json(tem_list)+"}" return temp_str obj = MyDict() obj[‘k1‘] = 123 obj[‘k2‘] = 456 obj[‘k3‘] = 789 print(obj)
6.__doc__
表示类的描述信息
class Foo: """ 描述类信息,这是用于看片的神奇 """ def func(self): pass print Foo.__doc__ #输出:类的描述信息
7.__module__ 和 __class__
__module__ 表示当前操作的对象在那个模块
__class__ 表示当前操作的对象的类是什么
8.__del__
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
四.异常处理
在编程过程中为了增加友好性在程序出现bug时一般不会直接将错误页面直接展现在用户面前,而是实现一个十分友好的提示界面,异常处理用try 和 except 基本语法如下:
try: pass except Exception,ex: pass
1.基本异常处理:
#异常处理 while True: num1 = input("num1:").strip() num2 = input("num2:").strip() try: num1 = int(num1) num2 = int(num2) result = num1 + num2 print("两个数字相加的结果是:%s"% result) except Exception as e: print(e) ##单输入为非整数时报错如下: num1:w num2:3 invalid literal for int() with base 10: ‘w‘ # 这里为报错内容 num1:
如上的Exception为异常处理的一种,常用异常种类有如下几种:
AttributeError 试图访问一个对象没有的属性,比如foo.x,但是foo没有属性x IOError 输入/输出异常;基本上是无法打开文件 ImportError 无法引入模块或包;基本上是路径问题或名称错误 IndentationError 语法错误(的子类) ;代码没有正确对齐 IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5] KeyError 试图访问字典里不存在的键 KeyboardInterrupt Ctrl+C被按下 NameError 使用一个还未被赋予对象的变量 SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了) TypeError 传入对象类型与要求的不符合 UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量, 导致你以为正在访问它 ValueError 传入一个调用者不期望的值,即使值的类型是正确的 常用异常
更多的异常总类如下:
ArithmeticError AssertionError AttributeError BaseException BufferError BytesWarning DeprecationWarning EnvironmentError EOFError Exception FloatingPointError FutureWarning GeneratorExit ImportError ImportWarning IndentationError IndexError IOError KeyboardInterrupt KeyError LookupError MemoryError NameError NotImplementedError OSError OverflowError PendingDeprecationWarning ReferenceError RuntimeError RuntimeWarning StandardError StopIteration SyntaxError SyntaxWarning SystemError SystemExit TabError TypeError UnboundLocalError UnicodeDecodeError UnicodeEncodeError UnicodeError UnicodeTranslateError UnicodeWarning UserWarning ValueError Warning ZeroDivisionError 更多异常
更多异常种类
应用实例:
IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
dic = ["zhaowencheng", ‘wangmeimei‘] try: dic[10] except IndexError as ex: print(ex) #显示如下: list index out of range Process finished with exit code 0
KeyError 试图访问字典里不存在的键
dic = {"aaa":"111", "bbb":"222"} try: dic["ccc"] except KeyError as ex: print(ex)
ValueError 传入一个调用者不期望的值,即使值的类型是正确的
s1 = ‘hello‘ try: int(s1) except ValueError, e: print e ###显示 invalid literal for int() with base 10: ‘hello‘ Process finished with exit code 0
2.进阶异常处理:
对于上面叙述的所有例中,所有的异常处理只能处理指定的异常,如KeyError只能捕捉访问字典中不存在的key时的异常,却无法捕捉ValueError的类型的异常。当捕获不到对应的异常时
程序依然回直接报错,达不到想要的效果如下:
s1 = ‘hello‘ try: int(s1) except KeyError as e: print(e) #由于未能捕获到正确的err程序依然回直接报错,显示如下: Traceback (most recent call last): File "/Users/wenchengzhao/PycharmProjects/s13/day8/temp.py", line 328, in <module> int(s1) ValueError: invalid literal for int() with base 10: ‘hello‘ Process finished with exit code 1
关于如上的内容应该改如何操作呢,有一种笨的方法如就是捕获多种类型的err,
s1 = ‘hello‘ try: int(s1) except KeyError as e: print(e) except ValueError as e: print(e) except IndexError as e: print(e) . . . #写上更多的 err 类型。
但是这种方法非常笨拙,而且很难写全面,其实python中有一个万能异常Exception,他能捕获各种异常
s1 = ‘hello‘ try: int(s1) except Exception as e: print(e) ##显示如下 invalid literal for int() with base 10: ‘hello‘ Process finished with exit code 0
那么既然后万能异常其它异常存在的意义是什么呢?
万能异常时这样使用的:对于特殊处理或提醒的异常需要先定义,最后定义Exception来确保程序正常运行。如下:
s1 = ‘hello‘ try: int(s1) except KeyError as e: print ("键错误") except IndexError as e: print (‘索引错误‘) except Exception as e: print (‘错误‘)
3.其它异常结构
try: # 主代码块 pass except KeyError,e: # 异常时,执行该块 pass else: # 主代码块执行完,执行该块 pass finally: # 无论异常与否,最终执行该块 pass
执行顺序:
4.主动触发异常
try: raise Exception(‘错误了。。。(wencheng)‘) except Exception as e: print (e) #显示如下: 错误了。。。(wencheng) Process finished with exit code 0
5.自定义异常
class ZhaowenchengException(Exception): def __init__(self, msg): self.message = msg def __str__(self): return self.message try: raise ZhaowenchengException(‘我的异常‘) except ZhaowenchengException as e: print(e) #显示如下 我的异常 Process finished with exit code 0
6断言
python assert断言是声明其布尔值必须为真的判定,如果发生异常就说明表达示为假。可以理解assert断言语句为raise-if-not,用来测试表示式,其返回值为假,就会触发异常。
语法简单如下:
# assert 条件 assert 1 == 1 assert 1 == 2
五.设计模式之 ---- 单例模式
简单说来,单例模式(也叫单件模式)的作用就是保证在整个应用程序的生命周期中,任何一个时刻,单例类的实例都只存在一个(当然也可以不存在)。
单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并提供全局访问点。
下面用pytho代码来实现一个单例模式
class Foo: instance = None #标示用于判断实例是否存在 def __init__(self,name): #构造方法 self.name = name @classmethod #类方法 def get_instance(cls): #将类名作为参数传递进来 if cls.instance: #判断实例是否存在 return cls.instance #如果存在直接返回,不在创建,确保单实例 else: obj = cls() cls.instance = obj return obj #返回对象