一、引入
1.1 概述
python支持函数式编程也支持面向对象编程
1.2 什么是面向对象?
下面以一个例子来说明面向对象的问题
class Bar:--函数变为对象 def foo(self,name,age,gender,content): print(name,age,gender,content) obj=Bar() obj.foo(小明,10岁,男,上山去砍柴)
面向对象定义:
class class_name:
def def_name(self,arg):
print(arg)
return 1
中间人=类名() ret=中间人.方法名() print(ret)为1,谁调用这个函数这个值变返回给谁
由以上可知想执行函数要通过类对象间接访问
原函数执行:函数名(参数)
现函数执行:面向对象:要间接访问里面的内容 o=Bar() ----对象,实例,o.foo()
对比函数与对象:创建一个类,加载时在内存里有一个类的内存,在这个内存里再分个内存做方法,在另一个内存里创建一个中间人指向这个类内存,
它只能找到这个类里的方法
class Bar: def foo(self,arg): print(self,arg) z1 = Bar()//z1是指向Bar这个类的地址 print(z1)//是一个对象的内存地址 z1.foo(111)---self就是z1的内存地址 z2 = Bar()---也会传给self,不同的对象 中间人对象:中间人内部也能放东西,引用其地址加变量等 class Bar: def foo(self,arg): print(self,self.name,self.age,arg) z=Bar()---对象,在对象里添加值如下 z.name='alex'---注这里中间人里加东西不是中间人调用类里面的方法 z.age=18 print(z.foo(111))----中间人调用方法,同时方法也能取中间人放在里面的东西并打印,二者是相互的 上个实例:--封装 class Bar: def add(self,content): print(self.name,self.age,content)---可以拿不同对象里的值 def delete(self,content): print(self.name,self.age,content) def update(self,content): print(self.name,self.age,content) obj=Bar() obj.name='小明' obj.age=10 obj.add(上山去砍柴) obj.delete(开车去东北)------这时相同的值就不用改了,被装在了中间人里,只会那边会变的就行,不用相同的值重复打 构造方法: 特殊作用: 在 obj=类名()----做了二个事情:第一:创建对象 第二:通过对象执行类中的一个特殊方法,特殊方法用_init_(self)来定义的函数 class Bar: def _init_(self): print('123') def add(self,content): print(self.name,self.age,content) z=Bar() print(z)---会打印二个一个是内存地址一个是123,也可以在_init_函数里加参数,做初始化 def _init_(self,name,age):-----这个方法就叫构造方法 self.n=name self.a=age self.b=1 def add(self,content): print(self.n,content) z=Bar('alex',18)---除了给他值alex与18还有一个默认值1,类里的参数传给构造函数初始化数据 z.add('home')
二、面向对象三大特性
类里的方法创建如下:
构造方法,__init__(self,arg)
obj = 类('a1')----字段
普通方法
obj = 类(‘xxx’)
obj.普通方法名()
2.1 面向对象三大特性之一:封装
class Bar: def __init__(self, n,a): self.name = n self.age = a self.xue = 'o' b1 = Bar('alex', 123) b2 = Bar('eric', 456)
适用场景:如果多个函数中有一些相同参数时,转换成面向对象,封装到_init_
class DataBaseHelper: def __init__(self, ip, port, username, pwd):--连数据库需要的 self.ip = ip self.port = port self.username = username self.pwd = pwd def add(self,content): # 利用self中封装的用户名、密码等 链接数据 print('content') # 关闭数据链接 def delete(self,content): # 利用self中封装的用户名、密码等 链接数据 print('content') # 关闭数据链接 def update(self,content): # 利用self中封装的用户名、密码等 链接数据 print('content') # 关闭数据链接 def get(self,content): # 利用self中封装的用户名、密码等 链接数据 print('content') # 关闭数据链接 s1 = DataBaseHelper('1.1.1.1',3306, 'alex', 'sb')
2.2 面向对象三大特性之二:继承
1. 继承:可多层继承
class 父类: pass class 子类(父类): pass 例:class father:----父类,基类 def 篮球(self): pass class son(father):---son就是子类,也叫派生类,这时父类的所有def子类都可以执行 def 抽烟(self): pass
2.重写:如何重写父类的方法?
父类里的某些方法,子类自己定义一个与其名字一样的,那只能执行子类的
3.self永远是执行改方法的调用者
4.如何子类与父类的二个相同方法都要执行?
class father:----父类,基类 def 篮球(self): pass class son(father):---son就是子类,也叫派生类,这时父类的所有def子类都可以执行 def 抽烟(self): pass def 篮球(self): super(son,self).篮球()---也可以是父类的其它方法,子类执行父类的方法 pass 法1:super(子类, self).父类中的方法(...) 法2:父类名.父类中的方法(self,...)
5.python支持多继承
a. 左侧优先 class son(father,father1):------如果二个都是一个方法刚左侧优先,里面都是有方法f1 obj=son() obj.f1()---则执行左边 b. 一条道走到黑 如上father的f1是继承它的父类的就一条道走到黑 c. 同一个根时,根最后执行 二条路最后都指向同一个父类,先左边走到了倒数第二个就走第二条路,最后执行根
6.应用:新做的功能想使用开源的方法就可以使用继承
开放的: class father: def 篮球(self): pass 新的功能: class son(father): def 其它(self): pass
2.3 面向对象三大特性之三:多态
# Java:必须要指定变量类型 string v = 'alex' def func(string arg):---这个变量必须指定类型 print(arg) func('alex') func(123) # Python :是多态的不用指定 v = 'alex' def func(arg): print(arg) func(1) func('alex')
三、面向对象的高级用法
3.1 类成员分类
类成员: # 字段 - 普通字段,保存在对象中,执行只能通过对象访问 - 静态字段,保存在类中,执行可以通过对象访问 也可以通过类访问,在方法外类内定义的字段 # 方法 - 普通方法,保存在类中,由对象来调用,self=》对象 例: class Foo: def bar(self): print('bar') obj = Foo()---先找到self obj.bar()----对象调用 Foo.bar(obj)----对象当参数调用,这些都是普通方法 - 静态方法,保存在类中,由类直接调用 例: class Foo: def bar(self):----普通方法 print('bar') @staticmethod---它下面的方法就是静态方法了,就不用传对象了,可以直接类调用就行 def str(): print('123') Foo.str()--就用调用str方法了,就是类自己的所有固定值了而不在是对象的特有的值了,所有对象都有共同的值 - 类方法,保存在类中,由类直接调用,cls=》当前类,类直接调的是类,函数的参数是类 例: class Foo: def bar(self):----普通方法 print('bar') @classmethod def classmd(cls):---cls是类名不是对象 print('classmd') Foo.classmd()---加载的是类 应用场景: 普通方法:如果对象中需要保存一些值,执行某功能时,需要使用对象中的值 静态方法:不需要任何对象中的值
3.2 方法的其它属性装饰器使用
@property //一个装饰器,叫属性,作用是把普通方法的调用变为字段的调用 用于执行obj.per,如下 class Foo: @property def per(self): return 1 obj=Foo() r=obj.per----这个对象调用不带(),相当于字段的调用,想赋值怎么办 @per.setter //这些方法要自己实现 用于执行obj.per=123赋值,用于赋值 class Foo: def per(self,val): print(val) obj=Foo() obj.per=123 @per.deleter 用于删除:---函数做什么自己写不是真的用于删除什么的 def per(self): print(666) obj=Foo() del obj.per 所以不同的调用格式就执行不同的对应的函数
以上功能的作用:
比如分页: li = []---空列表 for i in range(100): li.append(i)---列表加100个值 while True: p = input('请输入要查看的页码:') p = int(p)--字符变为数字 start = (p-1)*10 end = p*10 print(li[start:end]) 升级把start end做为一个组件 class Pergination: def _init_(self,current_page) try:--异常处理 p=int(current_page) except Exception as e: p=1 self.page=int(current_page) @property def start(self): val = (self.page-1)*10 return val @property def end(self): val=self.page*10 return val 这样再调用时print(li[obj.start:obj.end])
四、 类的其它知识点
4.1 类成员修饰符
字段的权限:
1.公有成员
2.私有成员:外部不能直接访问
普通字段私有化 class Foo: def __init__(self,name,age): self.name = name self.age = age self.__age = age加上二个下划线,这个就访问不了了,但能在内部访问 def show(self): return self.__age----这时就可以访问的到了 obj = Foo() obj.name #obj.age obj.__age---这个就访问不了,这个就是私有成员 静态字段私有化 class Foo: __v='123' def __init__(self): pass def show(self): return Foo.__v ret=Foo().show() 普通方法的公私有 class Foo: def __f1(self): return 123 def f2(self): r = self.__f1() return r obj = Foo() ret = obj.f2() print(ret)
4.2 特殊成员认识
1. __call__:对象后加()是执行特殊成员__call__的=Foo()()
class Foo: def __init__(self): print('init') def __call__(self, *args, **kwargs): print('call') # obj = Foo() # obj()-----对象后加()是执行特殊成员__call__的=Foo()()
2.数据类型相互转化
__int__ __str__
# s = "123"-----转为字符型相当于 s = str('123') # i = int(s)----字符转为数值 # print(i,type(i)) 那函数等能转化吗? class Foo: def __init__(self)://构造方法 pass def __int__(self): return 1111 def __str__(self): return 'alex' obj = Foo() print(obj, type(obj)) # int,对象,自动执行对象的 __int__方法,并将返回值赋值给int对象 r = int(obj)---int里加的对象会自动运行_int_方法,对象进行类型转化,这时对象的值不为地址了而是str方法定义的值 print(r) i = str(obj)---str里加的对象会自动运行_str_方法,对象进行类型转化,这时对象的值不为地址了而是str方法定义的值 print(i)-----直接返回的是类对象的值不是地址了
3. 对象相加要执行的特殊方法
class Foo: def __init__(self, name,age): self.name = name self.age = age def __add__(self, other): # self = obj1 封装了(alex,19) # other = obj2 封装了(eric,66) # return self.age + other.age----定义r返回的结果 #return Foo('tt',99) return Foo(obj1.name, other.age) def __del__(self): print('析构方法') # 对象被销毁()时,自动执行 obj1 = Foo('alex', 19) obj2 = Foo('eirc', 66) r = obj1 + obj2 # 两个对象相加时,自动执行第一个对象的的 __add__方法,并且将第二个对象当作参数传递进入 print(r,type(r))
4.析构方法 ---对象被销毁()时,自动执行
def __del__(self):---析构方法
5. *对象转为字典的形式
__dict__:# 将对象中封装的所有内容通过字典的形式返回,而非一个地址 class Foo: def __init__(self, name,age): self.name = name self.age = age self.n = 123 # obj = Foo('alex', 18) # d = obj.__dict__----对象的成员通过字典显示出来 # print(d) ['name':alex,'age':18] # ret = Foo.__dict__----类成员通过字典显示出来 # print(ret)
6. *索引 __getitem__ __setitem__ __delitem__
# li = [11,22,33,44] # li = list([11,22,33,44]) # li[3]----调用展示的方法 # li[3] = 666---调用修改的方法 # del li[2]----删除方法 class Foo: def __init__(self, name,age): self.name = name self.age = age def __getitem__(self, item): # return item+10 # 如果item是基本类型:int,str,索引获取 # slice对象的话,切片 if type(item) == slice: print('调用这希望内部做切片处理') else: print(item.start) print(item.stop) print(item.step) print('调用这希望内部做索引处理') def __setitem__(self, key, value): print(key,value) def __delitem__(self, key): print(key) li = Foo('alex', 18) r= li[8] # 自动执行li对象的类中的 __getitem__方法,8当作参数传递给item,后加个[] print(r) r=li[1:2:3]---切片,也用的是__getitem__方法,所以要在方法里判断,在python3里是这样的 li[100] = "asdf"--自动执行li对象的类中的 __setitem__方法,key是100,asdf是value del li[999]---自动执行li对象的类中的 __delitem__方法
7. __iter__:变为可迭代对象
class Foo: def __init__(self, name,age): self.name = name self.age = age def __iter__(self): return iter([11,22,33]) li = Foo('alex', 18)//这虽然是个地址,但它里面包含的有数据,只是返回的值地址,类方法可定义对象返回什么样的值 for i in li:------这个调用就会执行__iter__的方法,对象循环,是可迭代对象,这个是类里有这个方法时才能用的 print(i) # 如果类中有 __iter__ 方法,对象=》可迭代对象 # 对象.__iter__() 的返回值: 迭代器 # for 循环,若后是迭代器,直接执行next,一个一个的拿 # for 循环,若后是可迭代对象,先调用对象.__iter__()变为迭代器后再进行next # 1、执行li对象的类F类中的 __iter__方法,并获取其返回值 # 2、循环上一步中返回的对象
4.3 metaclass:类的祖宗
1.Python中一切事物都是对象
2. 类也是对象
class Foo: pass obj = Foo() # obj是对象,Foo类 # Foo类也是一个对象,它是type的对象
例: class Foo: def func(self): print(12) 以上这个代码解释器拿到后会进行如下处理: def funtion(self): print(12) Foo=type('Foo',(object,),{'func':funtion})----object:超级类所有的类都继承这个类。声明了一个类
3. 类都是type类的对象 type(..)
“对象”都是以类的对象 类()
问题:type创建类这个对象时执行的是__init__,这个是由C实现的,所以看不了,如果类不想用type创建如何做? class MyType(type):----MyType做为type的子类,所以先用MyType的__init__ def __init__(self,*args, **kwargs): # self=Foo,这时还没有obj所以它是Foo print(123) pass def __call__(self, *args, **kwargs): # self=Foo obj = self.__new__() self.__init__----Foo.__init__ python2:class Foo(object): __metaclass__ =MyType python3:class Foo(object,metaclass=MyType):------定义Foo用MyType这个类来创建不再用type了 def __init__(self): pass def __new__(cls, *args, **kwargs): return '对象'----这个对象就给r了,它是obj def func(self): print('hello wupeiqi') 1.结果是123 2.obj = Foo()----执行MyType的__call__方法,这里会调用__new__--在这里创建生成的obj
4.4 异常处理
1. 捕捉异常:try...except Exception as e:
try: #代码块,逻辑 inp=input("请输入序号:")----输入数字就走这个输入是字母就走下一个,都不会报错 i=int("inp")----出现错误后try下面的代码不会执行直接跳到下面的选择里 except Exception as e: #如果try里面出错就会创建一个Exception对象,对象中封装错误信息 #上述代码块如果出错,自动执行当前块的内容,这样代码就不会报错了 print(e)---打应错误信息 i = 1 print(i) 注:Exception是所有错误信息都能扑捉封装 IndexError:扑捉的是索引错误 ValueError:扑捉的是值的错误 IOError:IO错误 else: print(12)---不出错就执行 finally: print(34)---出不出错都执行 try: li=[11,22] li[999]----这个报错IndexError能扑到 int(se)----这个报错就扑不到程序就会报错 except IndexError as e: print(e)
2.主动抛出异常
try: # int('asdf') # 主动出发异常 # raise Exception('不过了...')--这是关键字段,这步自己报错,下面来捕捉,Exception报的是报错内容,这是个函数,可以自己定义函数 except Exception as e: print(e) 例: def db(): # return True return False def index(): try: r = input(">>") int(r) result = db() if not result: r = open('log','a') r.write('数据库处理错误') # 打开文件,记录日志 #raise Exception('数据库处理错误') except Exception as e: str_error = str(e) print(str_error) r = open('log', 'a') r.write(str_error) # 打开文件,记录日志 index()
3.自定义异常类,可自调用使用
class OldBoyError(Exception): def __init__(self, msg): self.message = msg def __str__(self)://类生成对象返回的结果 return self.message # obj = OldBoyError('xxx') # print(obj) try: raise OldBoyError('我错了...')---自定义函数报错 except OldBoyError as e: print(e)# e对象的__str__()方法,获取返回
4.断言
# assert 条件,断言,用于强制用户服从,不服从就报错,可扑捉,一般不扑捉
# assert 1==2------必须成立程序才能执行,否则一条都不会执行
# print(456)
4.5 反射
1.通过字符串的形式操作对象中的成员(用特定方法拿类或函数里的成员不用调用)
class Foo: def __init__(self, name,age): self.name = name self.age = age def show(self): return "%s-%s " %(self.name,self.age) def __int__(self): return 123 def __str__(self): return 'uuu' obj = Foo('alex', 18)---对象 r = int(obj) # 这个对象的数值是r = 123 u = str(obj) b = 'name' obj.__dict__['name']=obj.__dict__[b]-----拿出alex,这种方法比较麻烦为减化封闭一个方法如下 更好的写法:去什么东西里面获取什么内容? getatter(obj,'name') 总写法:反射,通过字符串的形式操作对象中的成员 # getattr---去什么东西里面获取什么内容,从对象里获取某字段,从某模块里获取某函数等 # hasattr---检测对象里是否有某成员 # setattr---修改对象里某成员的值 # delattr---删除对象里的某值 # 通过字符串的形式操作对象中的成员 # func = getattr(obj, 'show') # print(func) # r = func() # print(r) # print(hasattr(obj, 'name')) # obj.k1 # setattr(obj, 'k1', 'v1') # print(obj.k1) # obj.name # delattr(obj, 'name')---删除对象某值 # obj.name # 去什么东西里面获取什么内容 # inp = input('>>>')----只能输入name age # v = getattr(obj, inp) # print(v)----输入name得到的是alex的结果
2.实例
class Foo: stat = '123' def __init__(self, name,age): self.name = name self.age = age # 通过字符串的形式操作对象中的成员 r = getattr(Foo, 'stat') print(r) 操作模块也可以 import s2 # r1 = s2.NAME # print(r1) # r2 = s2.func() # print(r2) r1 = getattr(s2, 'NAME')----NAME是变量 print(r1) r2 = getattr(s2, 'func')---拿到func的地址,func是函数 result = r2() print(result) cls = getattr(s2, 'Foo')--Foo是类 print(cls) obj = cls() print(obj) print(obj.name) 一个网页,不同的按钮进入不同的网页,URL也不同,若第一个按钮对应模块s2.py 第二个对应s3.py 在s1中写入 import s2 import s3 inp = input('请输入要查看的URL:') if hasattr(s2, inp):---是否有这个值 func = getattr(s2, inp)得到s2的inp的值 result = func() print(result) else: print('404')
4.6 单例模式:用于使用同一份实例
class Foo: # def __init__(self, name,age): # self.name = name # self.age = age # obj = Foo() # obj对象,obj也成为Foo类的 实例,(实例化) # obj1 = Foo() # obj2 = Foo() # obj3 = Foo() 这个例子跟单例有什么关系,这上面是四个实例,用到四个内存 单例,用于使用同一份实例(对象) class Foo: def __init__(self, name,age): self.name = name self.age = age def show(self): print(self.name,self.age) v = None while True: if v: v.show()----只用这一个单例,只用一个实例 else: v = Foo('alex', 123) v.show() 改良 class Foo: __v = None @classmethod def get_instance(cls):---这个函数里的类 if cls.__v:----类时的私有静态字段 return cls.__v else: cls.__v = Foo() return cls.__v # 不要在使用 类()来创建一个对象 obj1 = Foo.get_instance()----创建对象,用类方法创建对象 print(obj1) obj2 = Foo.get_instance() print(obj2) obj3 = Foo.get_instance() print(obj3)