oop、try_except、单例模式
本节大纲:
面向对象特性:封装、继承、多态。
一:多态:python本身是多态,他的参数可以多种类型。可以是字符串、数字、列表等。当传入参数的时候,python可以判断参数的数据类型。
而在java C#中不是。需要指定参数的类型。实现多态,需要指定类型为父类、参数类型可以是父类和子类的类型来实现多态特性。
由于python本身的多态导致,由于类型的不确定,到时候在读源码的时候增加难度。这也算是python本身的一个缺点。
二:类的成员
字段:分普通字段(动态字段)和静态字段。
1 class test: 2 def __init__(self,name,age): 3 self.name=name 4 self.age=age 5 def show(self): 6 print(self.name,self.age) 7 evil 30
1 class test: 2 job='IT' 3 def __init__(self,name,age): 4 self.name=name 5 self.age=age 6 def show(self): 7 print(self.name,self.age) 8 9 10 11 obj=test('evil',30) 12 obj.show() 13 print(test.job)
a:访问方式:
默认是自己访问自己字段的。
普通字段属于对象,需要通过对象去访问。
静态字段属于类的(万不得已情况下,可以用对象通过类对象指针间接访问静态字段)。
b:
静态字段在程序加载的时候,就创建。
而普通字段(动态字段)在对象未创建的时候,该字段不会被创建和加载到内存中。
普通字段只能通过对象访问。
1 class province: 2 country='China' 3 def __init__(self,name): 4 self.name=name 5 def show(self): 6 print(self.name) 7 8 print(province.country) 9 China
说明:普通字段是属于对象的,每个对象的创建的时候,自动创建这个字段。
静态字段是属于类的,在类的创建的时候只需一份,每次调用都是调用类的中的静态字段。
也就是普通字段在对象的创建的时候生成,创建多少个对象,就创建多少普通字段,并保存在对象中。
而静态字段无论对象创建多少份,他只调用类中一份静态字段。
练习:
1 class province: 2 country='China' 3 def __init__(self,name): 4 self.name=name 5 def show(self): 6 print(self.name) 7 8 9 obj=province('Liaoning') 10 obj.show() 11 print(province.country) 12 Liaoning 13 China
c:使用场景
当对象有一个共同默认值的时候,可以使用静态字段来代替普通字段(动态字段)避免重复创建字段,浪费内存。
三:方法
静态方法、普通方法、类方法
1 class co: 2 name='ack' 3 def __init__(self,name):#普通方法必须要有self参数 4 self.name=name 5 def show(self):#普通方法 6 print(self.name) 7 @staticmethod##静态方法,参数可以自己指定没有必要参数 8 def look(name,age): 9 print(name,age) 10 @classmethod#类方法,必需参数cls 11 def cat(cls): 12 print(cls.name) 13 co.look('evil',23) 14 obj=co('tom') 15 obj.show() 16 co.cat() 17 evil 23 18 tom 19 ack
注意:
普通方法:必需参数:self 对象调用 obj. methodname()
静态方法:无必需参数。直接用类调用 类名.方法()
类方法:必需参数:cls 属于静态方法的特殊一种。也是直接用类调用。
总结:
也就是说,对象想调用非自己的字段和方法,需要通过类对象指针去类或者父类中找相应的方法和字段。
而类的字段(静态字段)和方法(静态方法、类方法)可以用类本身去调用,但是对象的普通字段无法调用。
三:属性。
个人理解:把方法用字段的形式进行访问。也就是说把方法去掉括号。
也就是说方法通过属性的方式伪造成字段访问形式。
通过类似字段的设置:重新赋值、删除动作触发执行相应属性的setter、deleter函数,至于函数的内容我们自定义,当用户执行这样的操作的时候,我们执行相应的动作。
方法一:
1 class Get_page: 2 def __init__(self,page_count): 3 self.page_count=page_count 4 @property 5 def get_page(self): 6 a,b=divmod(self.page_count,10) 7 if b==0: 8 return a 9 else: 10 return a+1 11 obj=Get_page(101) 12 res=obj.get_page 13 print(res) 14 11
1 class Get_page: 2 def __init__(self,page_count): 3 self.page_count=page_count 4 @property 5 def get_page(self): 6 a,b=divmod(self.page_count,10) 7 if b==0: 8 return a 9 else: 10 return a+1 11 @get_page.setter 12 def get_page(self,value): 13 print(value) 14 @get_page.deleter 15 def get_page(self): 16 print('del') 17 18 obj=Get_page(101) 19 res=obj.get_page 20 print(res) 21 obj.get_page=111 22 del obj.get_page 23 24 25 11 26 111 27 del
方法二:
通过方法property来实现属性。这种方式在源码里出现较多。
1 class Get_page: 2 def __init__(self,page_count): 3 self.page_count=page_count 4 def f1(self): 5 print('f1') 6 def f2(self,value): 7 print('f2') 8 def f3(self): 9 print('f3') 10 a=property(fget=f1,fset=f2,fdel=f3) 11 12 obj=Get_page(111) 13 obj.a 14 obj.a=1 15 del obj.a 16 f1 17 f2 18 f3
四:成员修饰符
只有(public)和私有(private)两种。
a:作用域:共有在类中、类外都可以进行调用。
私有只能类中调用,外部想引用必须通过内部方法进行调用。
b:语法:
私有:两个下划线:__
字段:
1 class Test: 2 def __init__(self,name): 3 self.__name=name#私有属性 4 def show(self): 5 print(self.__name)#只能内部方法进行调用 6 obj=Test('tom') 7 obj.show() 8 tom
不可以这样:
class Test: def __init__(self,name): self.__name=name def show(self): print(self.__name) obj=Test('tom') print(obj.__name)
会报:AttributeError: 'Test' object has no attribute '__name'
共有:
1 class Test: 2 def __init__(self,name): 3 self.name=name#就是共有字段。 4 def show(self): 5 print(self.name) 6 obj=Test('tom') 7 print(obj.name) 8 tom
方法:
1 class Test: 2 def __init__(self,name): 3 self.name=name 4 def __show(self):##私有方法 5 print(self.name) 6 def cat(self): 7 self.__show() 8 9 obj=Test('evil') 10 obj.cat()
外部想调用内部属性只能通过内部函数的间接调用实现外部的调用。
1 class Test: 2 def __init__(self,name): 3 self.name=name 4 @staticmethod 5 def __show(user):##私有静态方法。 6 print(user) 7 def cat(self): 8 Test.__show('tom') 9 tom
在其他语言中私有无法访问,但是python可以强制访问,最好不要这么操作,不符合python代码规范。方法:单下划线,对象._类名__属性名双下划线,没有属性. 。
1 class Test: 2 def __init__(self,name): 3 self.__name=name 4 @staticmethod 5 def __show(user): 6 print(user) 7 def cat(self): 8 Test.__show('tom') 9 10 obj=Test('evil') 11 obj._Test__show('op') 12 print(obj._Test__name) 13 op 14 evil
五:python特殊成员:
a:__del__ 析构方法。在类的对象回收之前,会执行该函数,如果该函数如果存在的话。
1 class Test: 2 def __init__(self,name):##构造方法。 3 self.__name=name 4 @staticmethod 5 def __show(user): 6 print(user) 7 def cat(self): 8 Test.__show('tom') 9 def __del__(self):###析构方法,在对象回收之前会执行该函数。 10 pass
b: __call__方法,执行方式:object()即对象后面加个括号。会自动调用该函数。
1 class Test: 2 def __init__(self,name): 3 self.name=name 4 def __call__(self, *args, **kwargs): 5 print('call') 6 obj=Test('OK') 7 obj()##执行方式 或者Test()()也是调用__call__方法。
c:__str__方法。执行方法是比如:print(obj)或者str(obj)会自动调用__strr__方法,如果没有该方法对象将返回对象在内存在地址。
注意:__str__返回的是字符串表达式。不是字符串形式,用str()转换。
1 class Test: 2 def __init__(self,name): 3 self.name=name 4 def __call__(self, *args, **kwargs): 5 print('call') 6 obj=Test('OK') 7 print(obj) 8 <s4.Test object at 0x00000000019B9D68>
1 class Test: 2 def __init__(self,name): 3 self.name=name 4 def __call__(self, *args, **kwargs): 5 print('call') 6 def __str__(self): 7 return self.name 8 9 obj=Test('evil') 10 print(obj) 11 evil
根据这个特性可以不用直接使用obj.name访问obj的name普通字段。可以用__str__方法,直接返回相应的属性。或者返回多个数据用字符串的拼接。
1 class Test: 2 def __init__(self,name,age,job): 3 self.name=name 4 self.age=age 5 self.job=job 6 def __call__(self, *args, **kwargs): 7 print('call') 8 def __str__(self): 9 return '%s_%s_%s'%(self.name,self.age,self.job) 10 11 obj=Test('evil',22,'IT') 12 print(obj) 13 evil_22_IT
d:__class__获取类名字:
1 class Test: 2 def show(self): 3 print('OK') 4 obj=Test() 5 print(obj.__class__) 6 <class '__main__.Test'>##main表示调用是在本程序内。
e:__dict__方法:如果是对象,获取对象的封装的数据以字典形式展示,如果是类获取类有的方法自带和自定义的方法,该方法比较重要,以后再form认证的时候会用到该方法。
1 class Test: 2 def __init__(self,name,job): 3 self.name=name 4 self.job=job 5 def show(self): 6 print('OK') 7 obj=Test('tom','IT') 8 print(obj.__dict__) 9 print(Test.__dict__) 10 {'job': 'IT', 'name': 'tom'} 11 {'__dict__': <attribute '__dict__' of 'Test' objects>, 'show': <function Test.show at 0x0127F468>, '__init__': <function Test.__init__ at 0x0127F420>, '__module__': 's2', '__doc__': None, '__weakref__': <attribute '__weakref__' of 'Test' objects>} 12 <class 's2.Test'>
f:__setitem__、__getitem__、__delitem__:操作对象,不同的动作会触发相应的方法,在写session的时候会用到这几个方法。
1 class Test: 2 def __init__(self,name,job): 3 self.name=name 4 self.job=job 5 def show(self): 6 print('OK') 7 def __setitem__(self, key, value): 8 print(key,value) 9 def __getitem__(self, item): 10 print(item) 11 def __delitem__(self, key): 12 print(key) 13 obj=Test('tom','IT') 14 obj['get'] 15 obj['tom']=123 16 del obj['del'] 17 get 18 tom 123 19 del
字典就是用上述的方法进行相应的操作的。来实现赋值、查看、删除等操作。
在python3中,当对象操作切片的时候也会调用__setitem__、__getitem__、__delitem__。如果是切片动作传进去实参是切片对象。
1 class Test: 2 def __getitem__(self, item): 3 print(type(item)) 4 print('get') 5 def __setitem__(self, key, value): 6 print(type(key),type(value)) 7 8 def __delitem__(self, key): 9 print(type(key)) 10 11 12 obj=Test() 13 obj[1:3] 14 obj[1:4]=[1,2,3,] 15 del obj[1:3] 16 <class 'slice'> 17 get 18 <class 'slice'> <class 'list'> 19 <class 'slice'>
可以根据传进去的对象类型进行相应的操作。当传进去是slice的时候,可以获取起起始、步长、结束。
class Test: def __getitem__(self, item): print(item.start) print(item.stop) print(item.step) def __setitem__(self, key, value): print(type(key),type(value)) def __delitem__(self, key): print(type(key)) obj=Test() obj[1:3:3] obj[1:4]=[1,2,3,] del obj[1:3] 1 3 3 <class 'slice'> <class 'list'> <class 'slice'>
g:__iter__方法:当一个对象被迭代:比如放在for循环里的时候,会自动执行对应类中的__iter__方法,元组、字符串、列表等在被迭代的时候,自动执行元组、字符串等类中的__iter__方法。
1 class Test: 2 def __iter__(self): 3 yield 1 4 yield 2 5 obj=Test() 6 for i in obj: 7 print(i)
__iter__方法 需要返回一个迭代器,而不是一个列表或者元组。。
1 class Test: 2 def __iter__(self): 3 return iter('avcc') 4 obj=Test() 5 for i in obj: 6 print(i)
h: isinstance判断对象是否是一个类的类型或者是否是该类的父类的类型。issubclass是判断一个类是否是另一个类的父类。
1 class Test_old: 2 def f1(self): 3 print('Test_old.f1') 4 5 class Test(Test_old): 6 def f1(self): 7 print('Test.f1') 8 9 10 obj=Test() 11 print(isinstance(obj,Test)) 12 print(isinstance(obj,Test_old)) 13 True 14 True
1 class Test_old: 2 def f1(self): 3 print('Test_old.f1') 4 5 class Test(Test_old): 6 def f1(self): 7 print('Test.f1') 8 9 print(issubclass(Test,Test_old)) 10 True
六:super()函数的应用以及源码的修改方式
a:super(),在执行子类的方法同时,也执行父类中相同名字的方法。
1 class Test_old: 2 def f1(self): 3 print('Test_old.f1') 4 5 class Test(Test_old): 6 def f1(self): 7 super(Test,self).f1()#执行自己类的父类中的f1方法。 8 print('Test.f1') 9 10 obj=Test() 11 obj.f1() 12 Test_old.f1 13 Test.f1
super参数可以省略自动去执行该类的父类中相同的方法。参考官网: https://docs.python.org/3.1/library/functions.html#super
1 class C(B): 2 def method(self, arg): 3 super().method(arg) # This does the same thing as: 4 # super(C, self).method(arg)
1 class Test_old: 2 def f1(self): 3 print('Test_old.f1') 4 5 class Test(Test_old): 6 def f1(self): 7 super().f1() 8 print('Test.f1') 9 Test_old.f1 10 Test.f1
使用场景:当我们接触别人代码或者源码的时候,需要对功能的改进的时候,需要遵循不修改源码的规则,使用类的继承以及super()的方法,可以在完全执行源码的功能的基础上,执行我们自己的代码。
如下例子是将无序字典转化成有序字典: 核心思想,在不修改源代码的情况下,进行代码的重构。
1 class SorDic(dict): 2 def __init__(self): 3 super().__init__() 4 self.lis=[] 5 def __setitem__(self, key, value): 6 super(SorDic,self).__setitem__(key,value) 7 self.lis.append(key) 8 9 def __str__(self): 10 tem_lis=[] 11 for key in self.lis: 12 value=self.get(key) 13 tem_lis.append('%s:%s'%(key,value)) 14 return '{'+','.join(tem_lis)+'}' 15 16 obj=SorDic() 17 obj['m']=2 18 obj['n']=3 19 print(obj)
七:设计模式:单例模式。
单例模式:单个实例模式,也就是创建单个对象实例。应用场景:数据库连接池,只需要创建一个连接池既可,无须创建多个。
1 class Singal: 2 instance=None 3 def __init__(self,name): 4 self.name=name 5 @classmethod 6 def get_instance(cls): 7 if cls.instance: 8 return cls.instance 9 else: 10 obj=cls('tom')###注意这个是常量问题。 11 cls.instance=obj 12 return cls.instance 13 14 obj1=Singal.get_instance() 15 obj2=Singal.get_instance() 16 print(id(obj1),id(obj2)) 17 5223568 5223568
注意:1:需要使用类方法,因为在对象未创建的时候可以调用类方法。
2:返回值是一个对象。
3:初始化__init__参数直接写入一个实参。
八:异常处理:
简单的异常处理:
1 while True: 2 try: 3 num1=input('Entre a number:') 4 num2=input('Entre a number:') 5 res=int(num1)+int(num2) 6 print(res) 7 except Exception as e: 8 print(e)
1:try:存放正常的代码。
2:except存放 Execption类的对象e e存储的是错误信息。并把错误信息打印输出。
except代码块 可以写Exception 捕捉所有错误,或者多个except来显式的捕获具体的错误。
1 while True: 2 try: 3 num1=input('Entre a number:') 4 num2=input('Entre a number:') 5 res=int(num1)+int(num2) 6 print(res) 7 lis=[1,2] 8 print(lis[100]) 9 except ValueError as e: 10 print(e) 11 except IndexError as e: 12 print(e) 13
a:通常做法是:通过except捕获多个具体的异常,最后用Execption捕获我们不需要的异常。注意顺序!
1 while True: 2 try: 3 num1=input('Entre a number:') 4 num2=input('Entre a number:') 5 res=int(num1)+int(num2) 6 print(res) 7 lis=[1,2] 8 print(lis[100]) 9 except ValueError as e: 10 print(e) 11 except IndexError as e: 12 print(e) 13 except Exception as e: 14 print(e)
b:常见的异常:
AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x IOError 输入/输出异常;基本上是无法打开文件 ImportError 无法引入模块或包;基本上是路径问题或名称错误 IndentationError 语法错误(的子类) ;代码没有正确对齐 IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5] KeyError 试图访问字典里不存在的键 KeyboardInterrupt Ctrl+C被按下 NameError 使用一个还未被赋予对象的变量 SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了) TypeError 传入对象类型与要求的不符合 UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量, 导致你以为正在访问它 ValueError 传入一个调用者不期望的值,即使值的类型是正确的
c:完整的异常处理块:
1 try: 2 pass 3 except Exception as e: 4 print(e) 5 else: 6 pass 7 finally: 8 pass
注意:
try代码块中,在执行过程中如果出现错误,则直接跳到execpt代码块中。
当执行try代码块的时候无报错,则执行else代码块,最后执行finally代码块。
如果执行try代码块的出现错误则直接跳到execpt代码块,最后执行finally代码块。
![](https://images2015.cnblogs.com/blog/959846/201607/959846-20160701221344437-223261090.png)
d:主动错误异常:raise 语句
1 try: 2 raise Exception('Error') 3 except Exception as e: 4 print(e) 5 finally: 6 pass
1 class Lmd(Exception): 2 3 def __init__(self, msg): 4 self.message = msg 5 6 def __str__(self): 7 return self.message 8 9 try: 10 raise Lmd('my error') 11 except Lmd as e: 12 print(e)
e:assert断言,assert condition 条件成立不报错,不成立报错。
1 assert 1=2 2 AssertionError