一、 面向对象特性之多态:
上一篇已经介绍了面向对象的三大特性的前两种(封装、继承),下面来说说第三种多态,在python中用不上,但在其他语言中很重要。
多态的意思就是多种类型、多种形态,比如字符类型,数字,字典,列表等。在python中定义类或函数时不需要,指定是那种数据类型全都支持,但是在java、c#等其他语言中需要指定如下实例:
def func(A arg) print(arg) arg :参数必须是A 类型,或A类型的子类
二、类成员:
类成员分为三大类:字段、方法、属性
1、字段
在类中字段分为静态字段和普通字段
普通字段(动态字段):存储在对象中,由对象调用
静态字段:存储在类中,由类调用,在代码加载时,就已经创建了
调用规则:
1、一般情况下自己访问自己;
2、普通字段,只能由对象访问
3、静态字段用类访问,(万不得已的时候也可以用对象访问,不建议使用)
两者区别如下:
class Foo: CC = 123 # CC是静态字段,保存在类中 def __init__(self): self.name = "tom" # name 就是普通字段,保存在对象中
实际调用如下:
class Province: contry="中国" #静态字段 def __init__(self,name): self.name=name #普通字段 sx=Province("河南") #静态调用 如 Province.contry #普通调用 如 sx.name print(Province.contry,sx.name) #显示结果 中国 河南
静态字段存储在类中, 只在内存中保存一份;
普通字段存储在每个对象中,需要在每个对象中都保存一份
应用场景:如果创建对象时,都要需要某一个相同的字段,可以把字段设置为静态字段,节省内存
2、方法
方法都属于类包括:
静态方法:属于类,由类来调用执行,无默认参数,等同于函数。创建方式: 方法上边加个@staticmethod
普通方法:由对象调用,至少需要一个self参数。执行普通方法时,self就是对象本身,自动将调用该方法的对象赋值给self
类方法:是静态方法的一种特殊形式。由类调用,至少要有一个参数cls,值为类名。创建方式: @classmethod
class Province: contry="中国" def __init__(self,name): #普通方法 self.name=name def show(self): print("普通方法") @classmethod def class_show(cls): #类方法 print(cls) @staticmethod def static_show(): #静态方法 print("静态方法") sx=Province("河南") #普通方法调用 sx.show() #类方法调用 Province.class_show() #静态方法调用 Province.static_show() #显示结果 普通方法 <class '__main__.Province'> #是这个类名 静态方法
相同点:由于所有的方法都属于类, 所以在内存中存储只保存一份。
不同点:由于各种方法的调用方式不同,调用方法时自动传入的参数不同。
给上面字段一样,在对象中也是可以调用静态方法和类方法的。不到万不得已还是不要用,要遵循变成原则。
3、属性
属性就是普通方法的变种
下面来看一下属性的定义:
class Foo: def show(self): print("普通方法") @property #定义属性 def prop(self): pass #调用 obj=Foo() obj.show() #调用方法 obj.prop #调用属性
注意:
定义时,在普通方法的基础上添加@property装饰器;仅有一个参数self
调用时,无需加括号
还有一种属性的定义调用方法:
class Foo(): @property def price(self): print('查询') @price.setter def price(self, value): print('设置') @price.deleter def price(self): print('删除') # ############### 调用 ############### obj = Foo() obj.price # 自动执行 @property 修饰的 price 方法,并获取方法的返回值 obj.price = 123 # 自动执行 @price.setter 修饰的 price 方法,并将 123 赋值给方法的参数 del obj.price # 自动执行 @price.deleter 修饰的 price 方法 #执行结果 查询 设置 删除
三、类成员修饰符
公有成员:就是在哪都能访问,
私有成员,只有在类的内部才能放问,其他都不能访问,继承关系也不能
定义私有成员:命名是前面是两个下划线,(特殊成员除外,例如:__init__、__call__、__dict__等)
如下:
class Foo: def __init__(self): self.name="公有字段" self.__fuck="私有字段"
四、类的特殊成员
1、__init__
构造方法,通过类创建对象时,自动触发
class Foo: def __init__(self,name): self.name = name # name 就是普通字段 self.job="IT" obj=Foo("tom") #自动执行类中的__init__方法 print(obj.name) print(obj.job) #结果 tom IT
2、__doc__
表示类的描述信息,就是注释
class Foo: ''' 描述信息 ''' def __init__(self,name): self.name = name # name 就是普通字段 self.job="IT" print(Foo.__doc__) #显示结果 描述信息
3、__del__
析构方法,用于对象在内存中垃圾回收时,自动触发执行
4、__call__
在对象后面加括号,执行
对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo: def __init__(self,name): self.name = name # name 就是普通字段 def __call__(self, *args, **kwargs): print("执行call方法") obj=Foo("test") #执行__init__方法 obj() #执行__call__方法
5、__module__、__class__
__module__表示当前操作的对象在那个模块
__class__ 表示当前操作的对象的类是谁
#!/usr/bin/env python3 # -*- coding: utf-8 -*- class Foo: ''' 这是类的描述信息, 由特殊成员 __doc__调用 ''' def __init__(self,name): self.name = name # print(self.name) def show_info(self): print(self.name) bin/modules.py内容
from bin import modules obj = modules.Foo('DBQ') print(obj.__module__) # 查看当前操作的对象属于哪个模块 print(obj.__class__) #查看当前操作的对象的类是哪个 #执行结果: bin.modules <class 'bin.modules.F1'>
6、__dict__
获取对象或类中的所有成员
class Foo: CC="test" def __init__(self,name): self.name = name # name 就是普通字段 #获取类中的所有成员 print(Foo.__dict__) #获取对象中的成员 obj=Foo("tom") print(obj.__dict__) #显示结果 {'__weakref__': <attribute '__weakref__' of 'Foo' objects>, 'CC': 'test', '__doc__': None, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'Foo' objects>, '__init__': <function Foo.__init__ at 0x00000041DAED61E0>} {'name': 'tom'}
7、__str__
如果一个类中定义了__str__方法,那么打印对象时,默认输出该方法的返回值
class Foo: CC="test" def __init__(self,name): self.name = name # name 就是普通字段 def __str__(self): return "hi tom" obj=Foo("tom") print(obj) #结果 hi tom
8、__iter__
用于迭代器,之所以列表、字典、元组可以进行for循环,是因为类型内部定义了__iter__
class Foo(): def __init__(self, sq): self.sq = sq def __iter__(self): return iter(self.sq) obj = Foo([11,22,33,44]) for i in obj: print(i)
9、__getitem__、__setitem__、__deltiem__
用于索引操作, 如字典。 分别表示获取、设置、删除数据。
class Foo(): def __getitem__(self, item): print("执行get操作,调用__getitem__方法") def __setitem__(self, key, value): print("执行set操作,调用__setitem__方法") def __delitem__(self, key): print("执行del操作,调用__delitem__方法") obj=Foo() obj['a1'] #get操作,自动触发__getitem__方法 obj["a1"]=[1,2,3,4,5] #set操作,自动调用__setitem__方法" del obj['a1'] #del操作,自动调用__delitem__方法 #结果 执行get操作,调用__getitem__方法 执行set操作,调用__setitem__方法 执行del操作,调用__delitem__方法
10、__getslice__、__setslice__、__delslice__
用于分片操作
class Foo(object): def __getslice__(self, i, j): print '__getslice__',i,j def __setslice__(self, i, j, sequence): print '__setslice__',i,j def __delslice__(self, i, j): print '__delslice__',i,j obj = Foo() obj[-1:1] # 自动触发执行 __getslice__ obj[0:1] = [11,22,33,44] # 自动触发执行 __setslice__ del obj[0:2] # 自动触发执行 __delslice__
在python3中还是运用的__getitem__、__setitem__、__deltiem__
class Foo(): def __getslice__(self, i, j): print('__getslice__',i,j) def __setslice__(self, i, j, sequence): print('__setslice__',i,j) def __delslice__(self, i, j): print('__delslice__',i,j) def __getitem__(self, item): print("执行get操作,调用__getitem__方法") def __setitem__(self, key, value): print("执行set操作,调用__setitem__方法") def __delitem__(self, key): print("执行del操作,调用__delitem__方法") obj = Foo() obj[-1:1] # 自动触发执行 __getslice__ obj[0:1] = [11,22,33,44] # 自动触发执行 __setslice__ del obj[0:2] # 自动触发执行 __delslice__ #结果 执行get操作,调用__getitem__方法 执行set操作,调用__setitem__方法 执行del操作,调用__delitem__方法
11. __new__ 、 __metaclass__
class F1: def __init__(self,name): self.name = name def show_info(self): print(self.name) obj = F1('tom') print(type(obj)) print(type(F1)) # #执行代码结果: # <class '__main__.F1'> #表示 obj对象由 F1类实例化而来 # <class 'type'> #表示 F1类由 type 类创建
python中一切介对象,上述代码中obj是F1的一个对象,那么可以推理F1其实也是一个对象
所以,obj对象时F1类的一个实例,F1类对象时type类的一个实例,F1类对象时通过type类构造方法创建
创建方法有两种普通方法、特殊方法:
普通方法:
class F1: def __init__(self,name): self.name = name def show_info(self): print(self.name)
特殊方法:
def show_info(self): print('tom') F1 = type('F1',(object,),{'show_info':show_info}) #第一个参数: 类名 #第二个参数: 当前类的基类 #第三个参数: 类成员 obj = F1() obj.show_info()
==》 类 是由 type 类实例化产生
那么问题来了,类默认是由 type 类实例化产生,type类中如何实现的创建类?类又是如何创建对象?
答:类中有一个属性 __metaclass__,其用来表示该类由 谁 来实例化创建,所以,我们可以为 __metaclass__ 设置一个type类的派生类,从而查看 类 创建的过程。
五、面向对象其他相关知识
1、isinstance(obj,cls)
检查是否obj是否是类 cls 的对象
判断一个对象是不是类创建的()实例,返回布尔值,继承的父类也为真
class F1: pass class F2(F1): pass obj = F2() print(isinstance(obj,F1)) #查看是否是父类的实例, 为真 print(isinstance(obj,F2)) #查看是否是F2类的实例, 为真
2、issubclass(F1,F2)
检查F1是否是F2的子类
查看是否是某类的子类
class F1: pass class F2(F1): pass obj = F2() print(issubclass(F2,F1)) #查看F2是否是F1的子类, 为真 print(issubclass(F1,F2)) #查看F1是否是F2的子类, 为假
3、 super
扩展别人的源码 ,尽量不在源码中修改
class C1: def f1(self): print('C1.f1') class C2(C1): def f1(self): super(C2,self).f1() #在执行C2代码之前,执行C1中的f1方法 也就是C2父类的f1方法 print('C2.f1') obj = C2() obj.f1() #结果 C1.f1 C2.f1
五、异常处理与捕获
1、异常处理基础
增加友好性,在程序出现bug中一般不会将错误信息显示给用户,而是显示一个页面
while True: num = input('请输入你一个或多个整数: ').strip() try: num = int(num) print('你输入的数字是: %d'%num) except Exception: print('%s, 你输入的不是一个整数格式!'%Exception) # 如果输入的是一个整数类型,将返回输入的号码 # 如果输入的是其他的类型,如字符串、浮点数等,会提示用户输入的不是一个整数格式! ####执行结果: 请输入你一个或多个整数: 123 你输入的数字是: 123 请输入你一个或多个整数: a <class 'Exception'>, 你输入的不是一个整数格式! 请输入你一个或多个整数: 1. <class 'Exception'>, 你输入
2.异常处理
捕获异常可以使用 try / except语句。try: 用来检测语句块中的错误,从而让 except中语句捕获的异常信息并处理。
打开一个文件,往文件中写入内容,并且没有发生异常:
try: f = open('test.txt','w') f.write('测试文件,用于测试异常捕获') except IOError: print('Error: 写入失败, 没有找到文件或者权限不足!') else: print('写入成功!') f.close() #执行结果: 写入成功! #文件内容: Daniel-Mac:blog daniel$ cat test.txt &&echo 测试文件,用于测试异常捕获
修改文件的权限没有写,而后在打开文件,往文件中写入内容,查看异常:
chmod -w test.txt try: f = open('test.txt','w') f.write('测试文件,用于测试异常捕获') except IOError: print('Error: 写入失败, 没有找到文件或者权限不足!') else: print('写入成功!') f.close() #再次执行代码: Error: 写入失败, 没有找到文件或者权限不足!
常用异常:
AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x IOError 输入/输出异常;基本上是无法打开文件 ImportError 无法引入模块或包;基本上是路径问题或名称错误 IndentationError 语法错误(的子类) ;代码没有正确对齐 IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5] KeyError 试图访问字典里不存在的键 KeyboardInterrupt Ctrl+C被按下 NameError 使用一个还未被赋予对象的变量 SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了) TypeError 传入对象类型与要求的不符合 UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量, 导致你以为正在访问它 ValueError 传入一个调用者不期望的值,即使值的类型是正确的
实例:
下标异常
dic = ["tom", 'jerry'] try: dic[10] except IndexError, e: print e
key异常
dic = {'k1':'v1'} try: dic['k20'] except KeyError, e: print e
元素异常
s1 = 'hello' try: int(s1) except ValueError, e: print e
对于上述实例,异常类只能用来处理指定的异常情况,如果没有指定异常则无法处理。
# 未捕获到异常,程序直接报错 tt = 234 try: str(tt) except IndexError,e: print e
如果想通吃各种异常,python中也提供了一个万能异常Exception,就能捕获任意异常,目的是保证程序能正常运行
tt = 234 try: str(tt) except Exception,e: print e
如果你还想要知道异常是什么,在那一块报错了,还有一个更周全的方案,如下:
s1 = 'hello world' try: int(s1) except KeyError: print('Error: Key错误!') except IndexError: print('Error: 索引错误!') except ValueError: print('Error: 值错误!') except Exception: print('Error: 出错了!') else: print('你的值是: %s'%s1)
六、设计模式,单实例
例模式,顾名思义,也就是单个实例的意思。
模式特点:保证类仅有一个实例,并提供一个访问它的全局访问点。
class Singleton: __instance = None #定义一个私有静态字段为初始值 def __init__(self,name): self.name = name def show(self): print(self.name) return 'test_instance' @classmethod def get_instance(cls): if cls.__instance: #如果字段内有值,直接返回字段值 return cls.__instance else: obj = cls('DBQ') #实例化 cls.__instance = obj #将对象赋值给字段 return cls.__instance #返回对象 a = Singleton.get_instance() b = Singleton.get_instance() print(a) print(id(a)) #内存地址和b相同 print() print(b) print(id(b)) #内存地址和a相同 # 后面再来几个对象,也是一样的! #代码执行结果: <__main__.Singleton object at 0x101b769b0> <__main__.Singleton object at 0x101b769b0>
单例模式的存在主要是保证当前内存中存在单个实例,避免内存资源浪费。