一、引入
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)