类和对象
面向对象
封装 | 对外部隐藏对象的工作细节 |
---|---|
继承 | 子类自动共享父类之间数据和方法的机制 |
多态 | 可以对不同类的对象调用相同的方法,产生不同的结果 |
类对象
“Python无处不对象”:当类定义完之后,自然就是类对象。在这个时候,你可以对类的属性(变量)进行直接访问
基本特征
封装
对象=属性(变量)+方法(函数)如果对象的属性跟方法名相同,属性会覆盖方法
类的属性名和方法名绝对不能相同
需要先创造类(class)(类名约定以大写字母开头,函数用小写字母开头),然后调用使用点操作符
class Turtle#只是名字
color="green" #属性
def climb(self): #方法
print("正在努力爬")
def bite(self):
print("咬你哦咬你哦")
#运行以上代码
t1=Turtle()
tt.bite()
输出:咬你哦咬你哦
t2=Turtle()
t2.legs=3
t2.legs
输出3,#但是不影响t1.legs等于4
self
代表上面的t1,t2
继承
子类(B),基类(A)
class A:
x=520
def hello(self):
print("你好呀")
class B(A):
pass
b.x
>>>520
b.hello()
>>>你好呀
如果B 里面有和类A 一样的属性和方法名,会覆盖掉
判断继承关系:
- isinsatance(b,B),返回True判断对象 a 是否为 类 A 的实例对象
isinsatance(b,A)也返回True
- 如果 objec t是 classinfo 的子类的一个实例,也符合条件
- 如果第一个参数不是对象,则永远返回False
- classinfo 可以是类对象组成的元祖,只要class与其中任何一个候选类的子类,则返回 True
- 如果第二个参数不是类或者由类对象组成的元祖,会抛出一个 TypeError 异常
- inssbuclass(B,A)返回True判断一个类是否为另一个类的子类
- 一个类被认为是其自身的子类
- classinfo 可以是类对象组成的元祖,只要 class 与其中任何一个候选类的子类,则返回 True
- 在其他情况下,会抛出一个 TypeError 异常
多重继承,调用时返回左边的父类,找不到继续往下找
组合,绑定
class Garden:
t=Turtle()
c=Cat()
def say(self):
self.t.say()#self用来绑定对象
self.c.say()
g=Garden()
g.say()
#判断Self
c=C()
c.get_self()
#查找对象的属性
c.__dict__
#往对象里面加属性(两种方法)
c.set_x(250)
c.__dict__['键名']=值
#如果不加self,类和属性x的值都不变,是会新建一个局部变量x
calss C:
x=100
def set_x(self,v):
x=v
c=C()
c.set_x(250)
c.x
>>>100
C.x()
>>>100
#如果对象c没有某个属性,会跟着类走
C.x=250 #不建议做
c.x
>>>250
#最小的类,可以当作字典,列表使用
class C:
pass
c=C()
c.x=5
c.y=6
print(c.x)
>>>5
重写(子类对父类重写函数)
调用未绑定的父类方法
class D(C)
class D:
def __init__(self,x,y,z):
C.__init__(self,x,y)
self.z= z
def add(self):
return self.add(self)+self.z
但是可能会出现钻石继承的问题
B1,B2继承A,C同时继承B1,B2,调用出来A 会出现两次
解决方法:调用super()函数,直接把B1,B2,C里面调用函数的地方变成super().函数名
#看函数,方法解析顺序(MRO)
b.mro()
super函数论坛有备用知识
,依赖mro
Mix-in()
如果给mixin起名是C,D同时继承C和B,那么遇到Super的时候如果自己没有函数,会调用到B里面
class Displayer:
def dispaly(self,message):
print(message)
class LoggerMixin:
def log(self,message,filename="logfile.txt"):
with open(filename,"a") as f:
f.wirite.lof(message)
def dispaly(self,message):
super().display(message)
self.log(message)
class Mysubclass(LoggerMixin,Displayer):
def log(self,message):
super()log(message,filename="subclasslog.txt")
subclass=Mysubclass()
subclass.display("this is a test")
#思路:先在Mysubclasss里面找display,然后第一句super跳到Loggermixin(message),然后又是一个super跳到Displayer打印,然后回到Loggermixin第二局self.log(message),跳到自己的log写入文件,名称是subclasslog
type()
用来创造类 type(“类名”,(父类名,),dict(x=250)(属性),value=520)
__init_subclass(cls,value)__
可以覆盖子类属性
多态
-
len函数也支持多态
-
类继承多态:重写(基类函数用pass,后面子类重新定义函数内容)
-
自定义函数,参数写成子类名字可以直接调用类里面的所有内容
def animal(x): x.intro() x.say() animal(c)
鸭子类型
鸭子类型(duck typing)是动态类型的一种风格。
在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定
“私有变量”
(通过某种方法,使得对象里面的属性和变量不被外界访问)
name mangling(名字修饰):前面加两个下划线,e.g.__x
执意访问x:对象名加下划线加类名加函数或者属性名:c._D__x
如果想要单独添加一个私有变量,就不会被改名隐藏
_单个下划线开头变量:仅供内部使用
单个下划线结尾变量: 为了保留字后面加"_"
__slots__
,锁属性,省空间
class C:
__solts__=["x","y"]
def init__(self,x):
self.x=x
继承自父类的__slots__
属性不会在子类中生效
魔法方法:类实例化时被自动调用
创建和注销
1.__init__(self[,....])
自动创建实例
init 特殊方法不应当返回除了 None 以外的任何对象
当我们的实例对象需要有明确的初始化步骤的时候,你可以在 init 方法中部署初始化的代码
构建对象:__new__(cls[,..])
然后传递给init
用到new:
class Capstr(str):#会继承字符串的所有操作
def __new__(cls,string):
string=string.upper()
return super().__new__(cls,string)#交给new的爸爸去做
cs=Capstr(Fish)
cs.lower
>>>'fish'
class C2F(float):
"摄氏度转换为华氏度"
def __new__(cls, arg=0.0):
return float.__new__(cls, arg * 1.8 + 32)
继承不可变类型时,重写new比较方便,比如
return str.__new__(cls,string)
2.__del__
销毁函数
class Capstr(str):
def __init__(self):
print("我来了")
def __del__(self):
print("我走了")
c=C()
>>>我来了
del c #只在不被引用的时候发生
>>>我走了
重生
#方法1:用全局变量
class D:
def __init__(self):
print("我来了")
def __del__(self):
global x
x=self
d=D()
#方法2,闭包函数:参数传递:self保存在外部函数x变量
class D:
def __init__(self,name,func):
self.name=name
self.func=func
def __del__(self):
self.func(self)
def outter():#闭包
x=0
def inner(y=None):
nonlocal x
if y:
x=y
else:
return x
return inner
f=outter()
e=D("我",f)
del e
g=f()
g.name
>>>我
运算相关
- 工厂函数:工厂函数,其实就是一个类对象。当你调用他们的时候,事实上就是创建一个相应的实例对象
class S(str):
def __add__(self,other):
return len(self)+len(other)
s1=S("Fish")
s2="Silly"
s1+s2
>>>9
#中文也能变整数
class ZW:
def __init__(self,num):
self.num=num
def __int__(self):
try:
return int(self.num)
except ValueError:
zw={"零":0,"一":1}
result=0
for each in self.num:
if each in zw:
result+=zw[each]
else:
result+=int(each)
result *= 10
return result // 10
反算数运算
调用前提:当运算时,如果左侧的对象和右侧对象是不同类,并且左侧对象没有对应的算数运算方法或者这个方法返回NotImplemented,那么寻找右侧对象是否有反算术运算并执行
增强赋值运算
s1+=s2
重新定义一个相同名字的类会覆盖
位运算
与& 只有两个数二进制相同位置都一样结果对应的位才是1,否则是0,
或| 只要其中某个位是1,结果对应位就是1
非~ 按位取反 补码
异或^ 某个位不一样的话就是1
左移 运算对象<<移动位数
比如1000左移两位是100000,结界在最右边,左移加进来两个0
右移 运算对象>>移动位数
移动位数必须是正数
优先级:或,异或,与优先级依次递增,非和正负号是一个优先级
math和index
ulp()表达math的最低有效位
index:对象作为索引值被访问
class C:
def __index__(self):
print("被拦截了")
return 3
c=C()
s="Fish"
s[C]
>>>被拦截了
bin(c) #返回二进制形式
被拦截了
'0b11'
比较
__lt__
拦截小于号<
__gt__
拦截大于号>
__eq__
拦截等于号=
字符串比较初始是比较编码大小
如果想要拦截不能实现,可以定义函数以后在下面写`le=None```
属性访问
函数和对应的魔法方法:
-
hasattr(对象,“属性名”),判断对象是否有某个属性,返回True或者False,注意属性名要用字符串格式
-
getattr(对象,“属性名”[, default]),得到属性的内容,否则返回Default内容
__getattibute__
(self,attrname)class C: def __init__(self,name,age): self.name=name self.__age=age def __getattribute__(self,attrname): print("拿来吧你") return super().__getattribute__(attrname) c=C("我",18) getattr(c,"name") >>>拿来吧你 我 c._C__age >>>拿来吧你 18
__getattr__
只在用户获取一个不存在的属性的时候才被触发,会拦截然后执行下面语句
class C:
def __init__(self,name,age):
self.name=name
self.__age=age
def __getattribute__(self,attrname):
print("拿来吧你")
return super().__getattribute__(attrname)
def __getattr__(self,attrname):
if attrname==Fish:
print("I love fish")
else:
raise AttruibuteError(attrname)
c=C("我",18)
c.Fish
>>>
拿来吧你
I love fish
#献给getattribute然后return到getattr
-
setattr(对象,“属性名”,属性值)
__setattr()__
#死亡螺旋: class D: def __setattr__(self,name,value): self.name=value d.D() d.name="我" >>>报错#因为赋值了两次 #解决方法,可以给super也可以用下面的 class D: def __setattr__(self,name,value): self.__dict__[name]=value d.D() d.name="我" d.name
-
delattr(对象,“属性名”)删除
__delattr__
和上面同理,要小心死亡螺旋
property函数
property() 函数允许编程人员轻松、有效地管理属性访问
class C:
def __init__(self):
self._x=250
def getx(self):
return self._x
def setx(self,value):
self._x=value
def delx(self):
del self._x
x=property(getx,setx,delx)
c=C()
c.x
>>>250
可以作为装饰器,使得创建只读函数变得很简单
class D:
def __init__(self):
self._x=250
@property
def x (self):
return self._x
#只读的话不带下面的
@x.setter
def x (self,value):
self._x=value
@x.deleter
def x(self):
del self._x
d=D()
d.x
索引,切片
对象对索引时,python会调用__getitem__(self,index)
拦截索引,切片和获取操作
slice函数用法:和字符串一样只不过空的地方要写None
s[slice(7,None)]
迭代器
__iter__(self)
一个容器如果是迭代器,那必须实现iter魔法方法,这个方法实际上就是返回迭代器本身
列表里没有next(),不是一个迭代器,是个可迭代对象
__next__(self)
真正用来迭代
自己的迭代器:
class Double:
def __init(self,start,stop):
self.value=start-1
self.stop=stop
def __inter__(self):
return self
def __next__(self):
if self.value==self.stop:
raise StopIteration
self.value+=1
return self.value*2
d.Double(1,5)
for i in d:
print(i,end=" ")
>>>2 4 6 8 10
#让对象干嘛,对象就干嘛,这个例子里面是让对象一直翻倍,直到Stop值
pos = turtle.move()
# 在迭代器中删除列表元素是非常危险的,经常会出现意想不到的问题,因为迭代器是直接引用列表的数据进行引用
# 这里我们把列表拷贝给迭代器,然后对原列表进行删除操作就不会有问题了^_^
for each_fish in fish[:]:
if each_fish.move() == pos:
# 鱼儿被吃掉了
turtle.eat()
fish.remove(each_fish)
print("有一条鱼儿被吃掉了...")
代偿
1.__contain__
a in b 优先找contain,然后找iter,next,最后找getitem,非零返回True
2.__bool__
优先bool,不行了换__len__
,结果是非零的话返回True
```__call(self[,位置参数,关键字参数])`可以像调用函数一样调用对象
class C:
def __call__(self):
print("hai")
c=C()
c()
>>>hai
字符串相关
__str__(self)
(str():参数转化成字符串,给人看)和__repr__(self)
(repr():对象转化为字符串,给程序看,是eval函数的反函数)
__repr__(self)
可以代偿__str__(self)
,而且使用场景更多
class C:
def __repr__(self):
return "I love you"
c=C()
repr(c)
>>>"I love you"
str()
>>>"I love you"
类方法
在def上一行加上@classmethod
静态
静态属性
在类中直接被定义的属性
class C:
count = 0 # 静态属性
def __init__(self):
C.count = C.count + 1 # 类名.属性名的形式引用
def getCount(self):
return C.count
静态方法
@staticmethod放在def前一行
使得可以用类名访问
静态方法不需要绑定对象
不涉及类属性和继承的时候用比较好
最大的优点是:不会绑定到实例对象上,换而言之就是节省开销
描述符
- 定义:实现了
__get__(self,instance,owner=None)
,__set__(self,instance,value)
,__delete__(self,instance)
三个方法的功能- get(self, instance, owner)
- 用于访问属性,它返回属性的值 - set(self, instance, value)
- 将在属性分配操作中调用,不返回任何内容 - delete(self, instance)
- 控制删除操作,不返回任何内容
- get(self, instance, owner)
- 应用:需要你设计一些更为复杂的操作来响应(例如每当属性被访问时,你也许想创建一个日志记录)。最好的解决方案就是编写一个用于执行这些“更复杂的操作”的特殊函数,然后指定它在属性被访问时运行。那么一个具有这种函数的对象被称之为描述符。
- 应用方法 :描述符被编写成独立的类,然后将描述符的实例对象赋值给类属性,从而可以实现对该属性读取,写入和删除的全方位拦截
- 举例
__get__(self,instance,owner=None)
把想要被管理类的属性作为对象,如类C种属性x,x=D()
self:x属性的值
instance:被拦截的属性所在类的对象
owner:被拦截属性所在的类
#不太好,不如property的方法
class D:
def __get__(self,instance,owner):
return instance._x
def __set__(self,intance,value):
intance._x=value
def __delete__(self,intance):
del instance._x
class C:
def __init__(self,x=250):
self._x=x
x=D()
c=C()
c.x
>>>250
#创建自己的proeperty方法
clss Myproperty():
def __init__(self,fget=None,fset=None,fdel=None):
self.fget=fget
self.fset=fset
self.fdel=fdel
def __get__(self,instance,owner):
return self.fget(instance)#instance:被拦截的属性所在类的对象
def __set__(self,intance,value):
self.fset(instance,value)
def __delete__(self,intance):
self.fdel(instance)
class C:
def __init__(self):
self._x=250
def getx(self):
return self._x
def setx(self,value):
self._x=value
def delx(self):
del self._x
x=Myproperty(getx,setx,delx)
c=C()
c.x
-
描述符只能应用于类属性,不能应用于对象属性
-
分类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A5qGlD5O-1677032350663)(Python面向对象+正则表达式.assets/image-20221124170037440.png)]
访问优先级(赋值会覆盖拦截):
__getattribute__
>数据描述符>实例对象属性>非数据描述符>类属性
6.除了三个方法之外的新方法:__set_name__(self,name,owner)
#原来的
class D:
def __init__(self,name):
self.name=name
def __get__(self,instance,owber):
print("get~")
return instance.__dict__.get(self,name)#instance:被拦截的属性所在类的对象
def __set__(self,instance,value):
print("set~")
instance.__dict__[self.name]=value
class C:
x=D("x")
c=C()
c.x=250
>>>set~
c.x
>>>get~
250
#优雅的
class D:
def __set_name__(self,owner,name):
self.name=name
def __get__(self,instance,owber):
print("get~")
return instance.__dict__.get(self,name)#instance:被拦截的属性所在类的对象
def __set__(self,instance,value):
print("set~")
instance.__dict__[self.name]=value
class C:
x=D()
c=C()
c.x=250
>>>set~
c.x
>>>get~
250
类描述器
类的装饰器
在类被实例化之前进行拦截来干预
类作为装饰器去装饰函数
生成器
功能:函数结束后还保留内容,让函数可以
做法:yield 代替 return语句,每次调用提供一个数据
特点:1.不走回头路 2.支持next语句
不可以用顺序索引
生成器表达式
列表推导式
生成器表达式:用普通小括号括起来(i for i in range (10))
- 可以作为函数的参数使用,外面可以不加小括号
定制容器
- 协议:
- 如果希望定制容器不可变,则只需要定义
__len__()
和__getitem__()
方法 - 如果希望可变,还需要定义
__setitem__()
和__deletitem__()
- 如果希望定制容器不可变,则只需要定义
正则表达式
-
用途:查找符合某些复杂规则的字符串
-
re模块
import re re.search(r'fish(可变)',"I love fish") #这里前面的fish也可以换成通配符'.',可以匹配除了换行符之外的任何字符 #如果就是想匹配点号,在前面加\
- 如果想匹配字符类中任何字符用[]包起来,中间还可以用-表示范围
- 重复匹配的话可以在需要匹配的字符后加{个数}
- \d表示0-9
-
特殊符号:
元字符: .^$+?{}[]|()
- 点号:通配符
- ^在开头才算
- $在结尾才算
- 反斜杠后面加上的是数字,那它还有两种用法:
• 如果跟着的数字是1~99,那么它表示引用序号对应的子组所匹
配的字符串。
• 如果跟着的数字是以0开头或者是三位数字,那么它是一个八进
制数,表示的是这个八进制数对应的ASCII字符
-
小括号(( ))本身是一对元字符,被它们括起来的正则表达
式称为一个子组。后面可以用来引用
>>> re.search(r"(FishC)\1", "FishC.com") #这里的\1表示引用前面序号为1的子组(也就是第一个子组),所 以r"(FishC)\1"相当于r"FishCFishC"
-
[]生成字符类,除了:
-
(1)小横杆(-),用它来表示范围:
>>> re.findall(r"[a-z]", “FishC.com”)
[‘i’, ‘s’, ‘h’, ‘c’, ‘o’, ‘m’]
>>> # findall()表示找出所有匹配的内容,并将结果返回为一个列表
(2)反斜杠(/),这里用于字符串转义,例如\n表示匹配换行符
号:
>>> re.search(r"[\n]", “FishC.com\n”)
<_sre.SRE_Match object; span=(9, 10), match=‘\n’>
(3)脱字符(^),用于表示取反的意思:
>>> re.findall(r"[^a-z]", “FishC.com”)
[‘F’, ‘C’, ‘.’]
-
反斜杠**+普通字母=**特殊含义
实用函数
-
需要重复使用某个正则表达式,那么可以先将该正则表达式编
译成模式对象。 使用re.compile()方法来进行编译:
\>>> p = re.compile("[A-Z]") \>>> p.search("I love FishC.com!") <_sre.SRE_Match object; span=(0, 1), match='I'> \>>> p.findall("I love FishC.com!") ['I', 'F', 'C']
-
re.search(pattern, string, flags=0)
flag不用写
-
要获得具体字符串:结果赋值给result,然后再result.group
- 如果结果中有元组,还可以按序号拿到
-
start()、end()和span()分别返回匹配的开始位置、结束位置和匹配的
范围
-
在findall()方法 中,如果给出的正则表达式包含了一个或者多个子组,就会返回子组中
匹配的内容。如果存在多个子组,那么它还会将匹配的内容组合成元组
的形式再返回。
-