面向对象3要素
1、封装
组装:将数据和操作组装到一起。
隐藏数据:对外只暴露一些接口,通过接口访问对象。比如驾驶员使用汽车,不需要了解汽车的构造细节,只需要知道使用什么部件怎么驾驶就行,踩了油门就能跑,可以不了解其中的机动原理。
2、继承
多复用,继承来的就不用自己写了
多继承少修改,OCP(Open-closed Principle),使用继承来改变,来体现个性
3、多态
面向对象编程最灵活的地方,动态绑定
封装
封装就是定义类,将属性和操作组织在类中
Python类定义
class ClassName:
语句块
1、必须使用class关键字
2、类名强烈建议使用大驼峰命名方式,即每个单词首字母大写。其本质上就是一个标识符
3、类定义完成后,就产生了一个类对象,绑定到了标识符ClassName上
class fn:
"""这是个类"""
x = 'abc' #这是个类属性
def foo(self):
"""这个是fn类中的foo方法"""
return __name__
print(fn, type(fn))
print(fn.__name__)#查看类的名字
print(fn.__doc__, fn.foo.__doc__, sep='\n')#查看类的文档和方法的文档
print(fn.foo)
print(fn.foo.__qualname__)#查看类中方法的全名
print(fn.x)#查看类属性
#结果
<class '__main__.fn'> <class 'type'>
fn
这是个类
这个是fn类中的foo方法
<function fn.foo at 0x000001A8846ABD90>
fn.foo
abc
类及类属性
类对象:类也是对象,类的定义执行后会生成一个类对象
类属性:类定义中的变量和类中定义的方法都是类的属性。上例中类Person的x和showme
类变量:属性也是标识符,也是变量。上例中类Person的x和showme
fn中,x、foo都是类的属性,__name__
、__doc__
是类的特殊属性
self 指代当前实例本身
实例化
a = fn()
使用上面的语法,在类对象名称后面加上一个括号,就调用类的实例化方法,完成实例化。实例化就真正创建一个该类的对象(实例instance)
class fn:
"""这是个类"""
x = 'abc'
def __init__(self, name, age):
print('init++++')
self.name = name
self.age = age
a = fn('Tom',12)
b = fn('dada')
print(a.name,a.age)
#结果
init++++
init++++
Tom 12
创建的实例,就会执行一下__init__
方法
__init__
方法
fn()
实例化后,要初始化,要调用的是__init__(self)
方法,可以不定义,如果没有定义会在实例化后隐式调用其父类的。
作用:对实例进行初始化
初始化函数可以多个参数,请注意第一个位置必须是self,例如__init__(self, name, age)
注意:__init__()
方法不能有返回值,也就是只能是return None
实例对象instance
类fn实例化后获得一个该类的实例,就是实例对象。下例中的a、b就是fn类的实例
__init__
方法的第一参数 self 就是指代某一个实例自身
执行a=fn('Tom', 12)
时,调用__init__()
方法。self.name就是a对象的name,name是保存在了a对象上,而不是fn类上。所以,称为实例变量
方法绑定
采用a.showage()
的方式调用,实例对象会绑定到方法上。这个self就是a,指向当前调用该方法的实例本身
a.showage()
调用时,会把方法的调用者tom实例作为第一参数self的实参传入__init__()
方法
class fn:
"""这是个类"""
x = 'abc'
def __init__(self, name, age=18):
print('init++++')
self.name = name
self.age = age
def showage(self):
print(f'{self.name} is {self.age} years old!')
a = fn('Tom', 12)
b = fn('dada')
print(a.name, a.age)
a.showage()
b.showage()
#结果
init++++
init++++
Tom 12
Tom is 12 years old!
dada is 18 years old!
实例变量和类变量
class fn:
"""这是个类"""
age = 10
def __init__(self, name):
print('init++++')
self.name = name
a = fn('Tom')
b = fn('dada')
print(a.name, a.age)
b.age=30
print(b.name, b.age)
#结果
init++++
init++++
Tom 10
dada 30
实例变量是每一个实例自己的变量,是自己独有的
类变量是类的变量,是类的所有实例共享的属性或方法
特殊属性
__name__ |
对象名 |
---|---|
__class__ |
对象类型 |
__dict__ |
对象的属性字典 |
__qualname__ |
类的限定名 |
Python中每一种对象都拥有不同的属性。函数是对象,类是对象,类的实例也是对象
class fn:
"""这是个类"""
age = 10
def __init__(self, name):
print('init++++')
self.name = name
a = fn('Tom')
b = fn('dada')
print(a.name, a.age)
b.age=30
print(b.name, b.age)
print(fn.__dict__)
print(a.__dict__)
print(b.__dict__)
#结果
init++++
init++++
Tom 10
dada 30
{'__module__': '__main__', '__doc__': '这是个类', 'age': 10, '__init__': <function fn.__init__ at 0x00000211A306BE18>, '__dict__': <attribute '__dict__' of 'fn' objects>, '__weakref__': <attribute '__weakref__' of 'fn' objects>}
{'name': 'Tom'}
{'name': 'dada', 'age': 30}
属性本质
class fn:
"""这是个类"""
height = 10
def __init__(self, name, age=40):
print('init++++')
self.name = name
self.age = age
a = fn('Tom')
b = fn('dada', 20)
print(fn.__dict__)
print(a.__dict__)
print(b.__dict__)
print(id(fn.height))
print(a.name, a.age, a.height,id(a.height))#40 10
print(b.name, b.age, b.height,id(b.height))#20 10
fn.age = 30
fn.height = 50
a.height=60
fn.w=30
print(fn.__dict__)
print(a.__dict__)
print(b.__dict__)
print(id(fn.height))
print(a.name, a.age, a.height,a.w,id(a.height))#30 50
print(b.name, b.age, b.height,b.w,id(b.height))#20 50
c=fn('yaya')
print(c.__dict__)
print(c.name, c.age, c.height,c.w)
#结果
init++++
init++++
{'__module__': '__main__', '__doc__': '这是个类', 'height': 10, '__init__': <function fn.__init__ at 0x0000020D8763BD90>, '__dict__': <attribute '__dict__' of 'fn' objects>, '__weakref__': <attribute '__weakref__' of 'fn' objects>}
{'name': 'Tom', 'age': 40}
{'name': 'dada', 'age': 20}
1621197200
Tom 40 10 1621197200
dada 20 10 1621197200
{'__module__': '__main__', '__doc__': '这是个类', 'height': 50, '__init__': <function fn.__init__ at 0x0000020D8763BD90>, '__dict__': <attribute '__dict__' of 'fn' objects>, '__weakref__': <attribute '__weakref__' of 'fn' objects>, 'age': 30, 'w': 30}
{'name': 'Tom', 'age': 40, 'height': 60}
{'name': 'dada', 'age': 20}
1621198480
Tom 40 60 30 1621198800
dada 20 50 30 1621198480
init++++
{'name': 'yaya', 'age': 40}
yaya 40 50 30
上例中,可以看到类属性保存在类的__dict__
中,实例属性保存在实例的__dict__
中,如果从实例访问类的属性,也可以借助__class__
找到所属的类,再通过类来访问类属性,例如a.__class__.age
总结
是类的,也是这个类所有实例的,其实例都可以访问到;
是实例的,就是这个实例自己的,通过类访问不到。
类变量是属于类的变量,这个类的所有实例可以共享这个变量。
对象(实例或类)可以动态的给自己增加一个属性(赋值即定义一个新属性)
实例.__dict__[变量名]
和实例.变量名
都可以访问到实例自己的属性
对实例访问来说,实例的同名变量会隐藏掉类变量,或者说是覆盖了这个类变量。但是注意类变量还在那里,并没有真正被覆盖
实例属性的查找顺序
指的是实例使用.点号
来访问属性,会先找自己的__dict__
,如果没有,然后通过属性__class__
找到自己的类,再去类的__dict__
中找
注意:如果实例使用__dict__[变量名]
访问变量,将不会按照上面的查找顺序找变量了,这是指明使用字典的key查找,不是属性查找
类方法和静态方法
前面的例子中定义的__init__
等方法,这些方法本身都是类的属性,第一个参数必须是self,而self必须指向一个对象,也就是类实例化之后,由实例来调用这个方法
普通函数
class fn:
def foo1():
print('+++')
print(fn.foo1)
fn.foo1()
fn().foo1()#这种调用是不被允许的
#结果
<function fn.foo1 at 0x0000025B2368BE18>
+++
fn().foo1()
TypeError: foo1() takes 0 positional arguments but 1 was given
fn.foo1()
可以放在类中定义,因为这个方法只是被fn
这个类管理的一个普通的方法,foo1
是fn
的一个属性而已
由于foo1
在定义的时候没有指定形参self,但不能用fn().foo1()
调用。原因是,fn()
是实例,实例调用的时候,由于做了实例绑定,那么就需要foo1
的第一个形参来接收绑定的实例
类方法-------->classmethod
内建函数,装饰器内部获取类型信息,并绑定这个类型
class fn:
@classmethod
def foo(cls):
print('===')
print(fn.foo)
fn.foo()
print(fn().foo())
fn().foo()
#结果
<bound method fn.foo of <class '__main__.fn'>>
===
<bound method fn.foo of <class '__main__.fn'>>
===
类方法
1、在类定义中,使用
@classmethod
装饰器修饰的方法 2、必须至少有一个参数,且第一个参数留给了
cls
,cls
指代调用者即类对象自身 3、
cls
这个标识符可以是任意合法名称,但是为了易读,请不要修改 4、通过
cls
可以直接操作类的属性
cls
只能使用类本身的属性,而__init__
是初始化属性,属于实例的属性,所以,cls
无法调用__init__
中的属性,如下例
class Animal:
__count = 100
_age=18
def __init__(self, name,male="female"):
self._name = name
self.male=male
@classmethod
def showcount(cls):
print(cls)
print(cls.__dict__.keys())
print(cls._name) #这个_name就无法调用
通过类、实例都可以非常方便地调用类方法。classmethod
装饰器内存将类或提取实例的类注入到类方法的第一个参数中
静态方法--------->staticmethod
装饰器,内建函数,调用函数,不会有绑定效果,不会注入第一个参数
class fn:
@staticmethod
def foo():
print('===')
print(fn.foo)
fn.foo()
print(fn().foo)
fn().foo()
静态方法
1、在类定义中,使用@staticmethod装饰器修饰的方法
2、调用时,不会隐式的传入参数
通过类、实例都可以调用静态方法,不会像普通方法、类方法那样注入参数
方法的调用
class fn:
def foo(self):
print('+++')
# a = fn('Tom')
a = fn()
print(fn.foo)
fn.foo(a)
print(id(fn.foo(fn())))
fn.foo([1,2,3])
print(fn().foo)
print(id(fn().foo()))
#结果
<function fn.foo at 0x0000026D9AD6BD90>
+++
+++
1378516272
+++
<bound method fn.foo of <__main__.fn object at 0x0000026D9AD75D68>>
+++
1378516272
总结:
类除了普通方法都可以调用
普通方法需要对象的实例作为第一参数
实例可以调用所有类中定义的方法(包括类方法、静态方法),普通方法传入实例自身,静态方法和类方法内部都要使用实例的类
访问控制
class Person:
def __init__(self, name, age=18):
self.name = name
self.__age = age
def __age_up(self, age):
self.__age += age
def get_age(self):
self.__age_up(20)
return self.__age
a = Person('Tom')
print(a.get_age())
print(a.name)
print(a.__age)#前面带双下划线的属性是私有属性,外部是无法使用的
print(a.__age_up)
#结果
38
Tom
print(a.__age)
AttributeError: 'Person' object has no attribute '__age'
print(a.__age_up)
AttributeError: 'Person' object has no attribute '__age_up'
私有(Private)属性
使用双下划线开头的属性名,就是私有属性
class Person:
def __init__(self, name, age=18):
self.name = name
self.__age = age
def __age_up(self, age):
self.__age += age
def get_age(self):
print(self.name)
return self.__age
a = Person('Tom')
print(a.get_age())
print(a.name)
print(a.__dict__)
a.name='berr'
a.__age=100
print(a.__age)
print(a.get_age())
print(a.__dict__)
#结果
Tom
18
Tom
{'name': 'Tom', '_Person__age': 18}
100
berr
18
{'name': 'berr', '_Person__age': 18, '__age': 100}
通过实验可以看出,外部已经访问不到__age
了,age
根本就没有定义,更是访问不到
希望通过a.__age=100
来修改私有属性,是无法实现的,通过字典可以看到,私有属性__age
是以下划线+类名+私有属性(例如_Person__age
)的形式存在字典中的,a.__age=100
只是为a这个实例添加了一个属性而已
私有成员
在Python中,在类变量或实例变量前使用两个下划线的变量,称为私有成员,包括私有属性、私有方法
上例中的__age
就是私有成员,无法通过a.__age
来调用,但是可以通过内部方法返回对应成员,达到调用的目的
def get_age(self):
return self.__age
私有变量的本质
秘密都在__dict__
中,原来私有成员都被改了名。
私有变量的本质:
类定义的时候,如果声明一个实例变量的时候,使用双下划线,Python解释器会将其改名,转换名称为_类名__变量名的名称,所以用原来的名字访问不到了
{'name': 'berr', '_Person__age': 18, '__age': 100}
哪怕后来自己定义了a.__age=100
,但是依然不是改变的私有变量,只是人为又添加了一个属性而已
保护成员
在类变量或实例变量前使用一个下划线的变量,称为保护成员
class Person:
def __init__(self, name, age=18):
self._name = name
self.__age = age
def _get_age(self):
return self.__age
def get_name(self):
print(self._get_age())
return self._name
a = Person('Tom')
print(a._get_age())
print(a._name)
print(a.__dict__)
#结果
18
Tom
{'_name': 'Tom', '_Person__age': 18}
可以看出,保护成员没有改变名称,解释器不做任何特殊处理。
这只是开发者共同的约定,看见这种变量,就如同私有变量,不要直接使用
补丁
可以通过修改或者替换类的成员。使用者调用的方式没有改变,但是,类提供的功能可能已经改变了
猴子补丁(Monkey Patch):
在运行时,对属性、方法、函数等进行动态替换。其目的往往是为了通过替换、修改来增强、扩展原有代码的能力
主程序
from t1 import fn
from t2 import foo
def f():
fn.chengji = foo
f()
if __name__ == '__main__':
print(fn().chengji())
t1.py
class fn:
def chengji(self):
x = {'Chinese': 66, 'math': 77}
return x
t2.py
def foo(cls):
return {'Chinese':100,'math':100}
上例中,假设fn
类chengji
方法是从数据库拿数据,但是测试的时候,不方便。
为了测试时方便,使用猴子补丁,替换了chengji
方法,返回模拟的数据
属性装饰器
一般好的设计是:把实例的某些属性保护起来,不让外部直接访问,外部使用getter读取属性和setter方法设置属性
class Preson:
def __init__(self, name, age=20):
self.name = name
self.__age = age
@property
def age(self):
print('is getter——')
return self.__age
@age.setter
def age(self, value):
print('is setter***')
self.__age += value
@age.deleter
def age(self):
print('is deleter')
del self.__age
a = Preson('Tom')
print(a.age)
a.age = 30
print(a.age)
print(a.__dict__)
del a.age
print(a.__dict__)
#结果
is getter——
20
is setter***
is getter——
50
{'name': 'Tom', '_Preson__age': 50}
is deleter
{'name': 'Tom'}
特别注意:使用property
装饰器的时候这三个方法同名
property装饰器
后面跟的函数名就是以后的属性名。它就是
getter
。这个必须有,有了它至少是只读属性
setter
装饰器 与属性名同名,且接收2个参数,第一个是self,第二个是将要赋值的值。有了它,属性可写
deleter
装饰器 可以控制是否删除属性。很少用
property装饰器必须在前,setter、deleter装饰器在后
property装饰器能通过简单的方式,把对方法的操作变成对属性的访问,并起到了一定隐藏效果
也可以使用property
内建函数
效果是一样的
age = property(getage, setage, delage, 'is property')
第一个参数是getter
第二个参数是setter
第三个参数是deleter
第四个是__doc__
class Preson:
def __init__(self, name, age=20):
self.name = name
self.__age = age
def getage(self):
print('is getter——')
return self.__age
def setage(self, value):
print('is setter***')
self.__age += value
def delage(self):
print('is deleter')
del self.__age
age = property(getage, setage, delage, 'is property')
a = Preson('Tom')
print(a.age)
a.age = 30
print(a.age)
print(a.__dict__)
del a.age
print(a.__dict__)
也可以把第一个函数写成匿名函数的形式
age = property(lambda self:self.__age, setage, delage, 'is property')
对象的销毁
类中可以定义__del__
方法,称为析构函数(方法)。
作用:销毁类的实例的时候调用,以释放占用的资源。其中就放些清理资源的代码,比如释放连接。
注意这个方法不能引起对象的真正销毁,只是对象销毁的时候会自动调用它。使用del
语句删除实例,引用计数减1。当引用计数为0时,会自动调用__del__
方法。
由于Python实现了垃圾回收机制,不能确定对象何时执行垃圾回收
每次对象调用结束后,会调用一下__del__
方法
class Preson:
def __init__(self, name, age=20):
self.name = name
self.age = age
def __del__(self):
print('调用了__del__方法')
a = Preson('Tom')
print('='*20)
#结果
====================
调用了__del__方法
当引用计数并未清零的时候,会在程序结束时,执行__del__
方法
class Preson:
def __init__(self, name, age=20):
self.name = name
self.age = age
def __del__(self):
print('调用了__del__方法')
a = Preson('Tom')
a1=a
a2=a1
print('='*20)
del a2
del a1
print('-'*20)
#结果
====================
--------------------
调用了__del__方法
当引用计数被清零,也会执行一次__del__
方法
class Preson:
def __init__(self, name, age=20):
self.name = name
self.age = age
def __del__(self):
print('调用了__del__方法')
a = Preson('Tom')
a1=a
a2=a1
print('='*20)
del a2
del a1
del a
print('-'*20)
#结果
====================
调用了__del__方法
--------------------
由于垃圾回收对象销毁时,才会真正清理对象,还会在回收对象之前自动调用__del__
方法,除非你明确知道自己的目的,建议不要手动调用这个方法
方法重载(overload)
其他面向对象的高级语言中,会有重载的概念。
所谓重载,就是同一个方法名,但是形式参数个数、类型不一样,就是同一个方法的重载
Python没有重载!
Python不需要重载!
Python中,方法(函数)定义中,形参非常灵活,不需要指定类型(就算指定了也只是一个说明而非约束),参数个数也不固定(可变参数)。一个函数的定义可以实现很多种不同形式实参的调用。所以Python不需要方法的重载。
或者说Python语法本身就实现了其它语言的重载
类的继承
面向对象三要素之一,继承Inheritance
在面向对象的世界中,从父类继承,就可以直接拥有父类的属性和方法,这样可以减少代码、多复用。子类也可以定义自己的属性和方法
class Animal:
def __init__(self, name):
self._name = name
def jiao(self):
print('{}在叫!'.format(self.__class__.__name__))
print(self.name)
@property
def name(self):
return self._name
a = Animal('animal')
a.jiao()
class Dog(Animal):
pass
b = Dog('xiaohuang')
b.jiao()
class Cat(Animal):
pass
c = Cat('xiaohua')
b.jiao()
#结果
Animal在叫!
animal
Dog在叫!
xiaohuang
Dog在叫!
xiaohuang
继承
class Cat(Animal)
这种形式就是从父类继承,括号中写上继承的类的列表。
继承可以让子类从父类获取特征(属性和方法)
父类
Animal就是Cat的父类,也称为基类、超类
子类
Cat就是Animal的子类,也称为派生类
定义
格式
class 子类名(基类1[,基类2,...]):
语句块
如果类定义时,没有基类列表,等同于继承自object
Python支持多继承,继承也可以多级。
查看继承的特殊属性和方法
特殊属性和方法 | 含义 |
---|---|
__bases__ |
类的基类元组 |
__base__ |
类的基类元组的第一项 |
__mro__ |
显示方法查找顺序,基类的元组 |
mro() 方法 |
显示方法查找顺序,基类的列表 |
__subclasses__() |
类的子类列表 |
上例的基础上
print(Animal.__bases__)
#结果
(<class 'object'>,)
print(Dog.__bases__)
#结果
(<class '__main__.Animal'>,)
print(Dog.__base__)
#结果
<class '__main__.Animal'>
print(Dog.__base__.__name__)
#结果
Animal
print(Animal.__subclasses__())
#结果
[<class '__main__.Dog'>, <class '__main__.Cat'>]
print(Dog.__base__.__subclasses__())
#结果
[<class '__main__.Dog'>, <class '__main__.Cat'>]
print(Dog.mro())
#结果
[<class '__main__.Dog'>, <class '__main__.Animal'>, <class 'object'>]
print(Dog.__mro__)
#结果
(<class '__main__.Dog'>, <class '__main__.Animal'>, <class 'object'>)
继承中的访问控制
class Animal:
__count = 100
_age=18
def __init__(self, name):
self._name = name
@classmethod
def showcount(cls):
print(cls)
print(cls.__dict__)
print(cls._age)
print(cls.__count)
class Cat(Animal):
"""is Cat doc"""
_age=20
__count = 200
a = Cat('mi')
a.showcount()
print('=='*20)
print(a.__dict__)
#结果
<class '__main__.Cat'>
{'__module__': '__main__', '__doc__': 'is Cat doc', '_age': 20, '_Cat__count': 200}
20
100
========================================
{'_name': 'mi'}
可以看出_age
是可以被继承的,而私有属性__count
无法被继承的!
从父类继承,自己没有的,就可以到父类中找
私有的都是不可以访问的,但是本质上依然是改了名称放在这个属性所在类或实例的__dict__
中
总结
继承时,公有成员,子类和实例都可以随意访问;私有成员被隐藏,子类和实例不可直接访问,但私有变量所在的类内的方法中可以访问这个私有变量。
Python通过自己一套实现,实现和其它语言一样的面向对象的继承机制
实例属性查找顺序
实例的__dict__
→类__dict__
→如果有继承→父类 __dict__
如果搜索这些地方后没有找到就会抛异常,先找到就立即返回了
方法的重写、覆盖override
class Animal:
def jiao(self):
print('Animla 叫')
class Cat(Animal):
def jiao(self):
super().jiao()
print('Cat 叫')
class Dog(Cat):
def jiao(self):
super(Cat, self).jiao()
print('Dog 叫')
class Pig(Dog):
def jiao(self):
super(Pig, self).jiao()
print('Pig叫')
b=Cat()
b.jiao()
print('=='*20)
a = Dog()
a.jiao()
print('=='*20)
c = Pig()
c.jiao()
#结果
Animla 叫
Cat 叫
========================================
Animla 叫
Dog 叫
========================================
Animla 叫
Dog 叫
Pig叫
class Dog(Cat):
def jiao(self):
super(Cat, self).jiao()
print('Dog 叫')
这个形式,就是对父类方法的重写或者加强
super(Cat, self).jiao()
是指,先调用Cat
父类的jiao()
方法,然后再在这个基础上执行自己的代码
如果是在自己父类的基础上加强,可以简写为
super().jiao()
类方法和静态方法
class A:
@classmethod
def clsdemo(cls):
print('A classmethod')
@staticmethod
def stademo():
print('A staticmethod')
class B(A):
@classmethod
def clsdemo(cls):
super(B, cls).clsdemo()
print('B classmethod')
@staticmethod
def stademo():
A.stademo()
print('B staticmethod')
b=B()
b.clsdemo()
b.stademo()
#结果
A classmethod
B classmethod
A staticmethod
B staticmethod
结果也是一样的,只是静态方法无法调用super(),因为没有第一参数
静态方法和类方法,是特殊的方法,也是类属性,这些方法都可以覆盖,原理都一样,属性字典的搜索顺序
继承时使用初始化
class Animal:
def __init__(self, a, b):
self.a = a
self.__b = b
class Cat(Animal):
def __init__(self, c, d):
super(Cat, self).__init__(c + d, c - d)
self.c = c
self.d = d
def get(self):
print(self.a)
print(self.__b)
print(self.c)
print(self.d)
b = Cat(20,5)
print(b.__dict__)
b.get()
#结果
{'a': 25, '_Animal__b': 15, 'c': 20, 'd': 5}
25
print(self.__b)
AttributeError: 'Cat' object has no attribute '_Cat__b'
私有属性__b
是无法调用的
class Animal:
def __init__(self, a, b):
self.a = a
self.b = b
# def jiao(self):
# print('Animla 叫')
class Cat(Animal):
def __init__(self, c, d):
super(Cat, self).__init__(c + d, c - d)
self.c = c
self.d = d
# def jiao(self):
# super().jiao()
# print('Cat 叫')
def get(self):
print(self.a)
print(self.b)
print(self.c)
print(self.d)
b = Cat(20,5)
print(b.__dict__)
b.get()
#结果
{'a': 25, 'b': 15, 'c': 20, 'd': 5}
25
15
20
5
class Animal:
def __init__(self, c):
self.__c = c
def get(self):
print(self.__c)
class Cat(Animal):
def __init__(self, c, d):
super(Cat, self).__init__(c)
self.__c = c + 1
self.d = d
b = Cat(20, 5)
print(b.__dict__)
b.get()
#结果
{'_Animal__c': 20, '_Cat__c': 21, 'd': 5}
20
多继承
Mixin
1、在Document中提供print方法
基类提供的方法可以不具体实现,因为它未必适合子类的打印,子类中需要覆盖重写。
基类中只定义,不实现的方法,称为“抽象方法”。
在Python中,如果采用这种方式定义的抽象方法,子类可以不实现,直到子类使用该方法的时候才报错
class Document:
def __init__(self,name):
self.name=name
def printable(self):
raise NotImplementedError()
class Word(Document):
def printable(self):
print(self.__class__.__name__,'打印',self.name)
class Pda(Document):
def printable(self):
print(self.__class__.__name__,'打印',self.name)
a=Word('hello world!')
a.printable()
b=Pda('hello world!')
b.printable()
#结果
Word 打印 hello world!
Pda 打印 hello world!
2、类装饰器
用装饰器增强一个类,把功能给类附加上去,哪个类需要,就装饰它
def printdoc(cls):
def print1(self):
print('+++++', self.name)
cls.print2 = print1
return cls
@printdoc
class Document:
def __init__(self, name):
self.name = name
@printdoc
class Word(Document):
pass
a = Word('hello world!')
print(a.__dict__)
print(Word.__dict__)
a.print2()
b = Document('123445566')
b.print2()
#结果
{'name': 'hello world!'}
{'__module__': '__main__', '__doc__': None, 'print2': <function printdoc.<locals>.print1 at 0x000002084319BF28>}
+++++ hello world!
+++++ 123445566
优点:
简单方便,在需要的地方动态增加,直接使用装饰器。
可以为类灵活的增加功能。
3、Mixin
class Document:
def __init__(self, name):
self.name = name
class Word(Document): pass
class printMixin:
def print1(self):
print(self.name)
class printable(printMixin, Word): pass
a = printable('hello world!')
a.print1()
print(a.__dict__)
print(printable.__dict__)
print(printMixin.__dict__)
print(printable.mro())
#结果
hello world!
{'name': 'hello world!'}
{'__module__': '__main__', '__doc__': None}
{'__module__': '__main__', 'print1': <function printMixin.print1 at 0x000001B1EA0FBE18>, '__dict__': <attribute '__dict__' of 'printMixin' objects>, '__weakref__': <attribute '__weakref__' of 'printMixin' objects>, '__doc__': None}
[<class '__main__.printable'>, <class '__main__.printMixin'>, <class '__main__.Word'>, <class '__main__.Document'>, <class 'object'>]
Mixin就是其它类混合进来,同时带来了类的属性和方法
Mixin本质上就是多继承实现的。
Mixin体现的是一种组合的设计模式
在面向对象的设计中,一个复杂的类,往往需要很多功能,而这些功能有来自不同的类提供,这就需要很多的类组合在一起。
从设计模式的角度来说,多组合,少继承
Mixin
类的使用原则
Mixin
类中不应该显式的出现__init__
初始化方法
Mixin
类通常不能独立工作,因为它是准备混入别的类中的部分功能实现
Mixin
类的祖先类也应是Mixin
类
使用时,Mixin
类通常在继承列表的第一个位置
例如class PrintableWord(PrintableMixin, Word): pass
通过把Mixin
类插在继承类的前面,达到插队的效果
{'__module__': '__main__', 'print1': <function printMixin.print1 at 0x000001B1EA0FBE18>, '__dict__': <attribute '__dict__' of 'printMixin' objects>, '__weakref__': <attribute '__weakref__' of 'printMixin' objects>, '__doc__': None}
通过这个字典可以看出,通过Mixin
插队,将方法print1
插入到了类的字典中
查看属性
dir(obj)对于不同类型的对象obj具有不同的行为:
如果对象是模块对象,返回的列表包含模块的属性名和变量名
如果对象是类型或者说是类对象,返回的列表包含类的属性名,及它的祖先类的属性名
如果是类的实例
有__dir__
方法,返回可迭代对象的返回值
没有__dir__
方法,则尽可能收集实例的属性名、类的属性和祖先类的属性名
如果obj不写,返回列表包含内容不同
在模块中,返回模块的属性和变量名
在函数中,返回本地作用域的变量名
在方法中,返回本地作用域的变量名
方法 | 意义 |
---|---|
__dir__ |
返回类或者对象的所有成员名称列表。dir() 函数操作实例就是调用__dir__() 。 |
如果dir([obj])参数obj包含方法__dir__()
,该方法将被调用。如果参数obj不包含__dir__()
,该方法将最大限度地收集属性信息
t1.py
print('-' * 40)
x = 10
class A:
def __init__(self, name):
self._name = name
self.__age = 20
self.gender = 'male'
print(__name__, dir())
#结果
----------------------------------------
__main__ ['A', '__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'x']
t2.py
import t1
from t1 import A
print('~'*40)
class B(A):
pass
print(t1.__name__,dir(t1))
print(__name__,dir())
print('='*40)
Tom=B('tom')
print(Tom)
print('dir---',dir(Tom))
print('sorted---',sorted(Tom.__dict__.keys()
| B.__dict__.keys()
| A.__dict__.keys()
| object.__dict__.keys()
))
#结果
----------------------------------------
t1 ['A', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'x']
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
t1 ['A', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'x']
__main__ ['A', 'B', '__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 't1']
========================================
<__main__.B object at 0x000001712FA81080>
dir--- ['_A__age', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_name', 'gender']
sorted--- ['_A__age', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_name', 'gender']
上面的例子可以看出dir()
内建函数返回的就是继承顺序的相关__dict__.keys()
排序结果
from t1 import A
class C(A):
def __dir__(self):
return [123]
c=C('tom')
print(dir(c))
print(dir(C))
#结果
[123]
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
__dir__
方法只影响对象的dir()
函数,不影响类的dir()
函数
内建函数
locals()
返回当前作用域中的变量字典
globals()
当前模块全局变量的字典
import inspect
print('-' * 40)
x = 10
class A:
print(inspect.stack()[0][3])
def show(self, name):
self._name = name
print(inspect.stack()[0][3])
y = 20
print(dir())
print(sorted(locals().keys()))
print(sorted(globals().keys()))
print('=' * 40)
def B():
print(inspect.stack()[0][3])
z = [1, 2, 3]
print(dir())
print(sorted(locals().keys()))
print(sorted(globals().keys()))
print('*' * 40)
A().show('b')
B()
print(dir())
print(sorted(locals().keys()))
print(sorted(globals().keys()))
#结果
----------------------------------------
A
show
['name', 'self', 'y']
['name', 'self', 'y']
['A', 'B', '__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'inspect', 'x']
========================================
B
['z']
['z']
['A', 'B', '__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'inspect', 'x']
****************************************
['A', 'B', '__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'inspect', 'x']
['A', 'B', '__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'inspect', 'x']
['A', 'B', '__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'inspect', 'x']
globals()
在哪里都是一样的
locals()
返回的则是本地的,与dir()
相同
魔术方法
分类:
创建、初始化与销毁
__new__
__init__
与__del__
可视化
hash
bool
运算符重载
容器和大小
可调用对象
上下文管理
反射
描述器
其他杂项
实例化
方法 | 意义 |
---|---|
__new__ |
实例化一个对象 该方法需要返回一个值,如果该值不是cls的实例,则不会调用 __init__ 该方法永远都是静态方法 |
class A:
def __new__(cls, *args, **kwargs):
print('A new', '-' * 30)
print(args)
print(kwargs)
return super().__new__(cls)
def __init__(self, name, age):
print('A init', '#' * 30)
self.name = name
self.age = age
a = A('Tom', age=12)
print(a)
#结果
A new ------------------------------
('Tom',)
{'age': 12}
A init ##############################
<__main__.A object at 0x0000022028821240>
__new__
方法很少使用,即使创建了该方法,也会使用return super().__new__(cls)
基类object的__new__
方法来创建实例并返回。
return super().__new__(cls)
很重要,将类本身传给父类的__new__
方法处理,由父类来调用__init__
可视化
方法 | 意义 |
---|---|
__str__ |
str()函数、format()函数、print()函数调用,需要返回对象的字符串表达。 如果没有定义,就去调用 __repr__ 方法返回字符串表达,如果__repr__ 没有定义,就直接返回对象的内存地址信息 |
__repr__ |
内建函数repr()对一个对象获取字符串表达。调用__repr__ 方法返回字符串表达如果 __repr__ 也没有定义,就直接返回object的定义就是显示内存地址信息 |
__bytes__ |
bytes()函数调用,返回一个对象的bytes表达,即返回bytes对象 |
class A:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return '调用str'
def __repr__(self):
return '调用repr'
def __bytes__(self):
return str(self).encode()
a = A('Tom', age=12)
print(a)
print('='*30)
print(str(a))
print('~'*30)
print([a],(a,),{a})
print(a,[str(a)],[a])
print(bytes(a))
#结果
调用str
==============================
调用str
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[调用repr] (调用repr,) {调用repr}
调用str ['调用str'] [调用repr]
b'\xe8\xb0\x83\xe7\x94\xa8str'
上例也可以看出
直接调用对象,如
print(a)
或者print(str(a))
都是调用__str__
方法 间接调用对象,如
print([a],(a,),{a})
是调用的__repr__
方法 如果
__str__
没有定义,__repr__
定义了,则无论直接调用还是间接调用,都是调用__repr__
方法 如果
__repr__
和__str__
都没有定义,则返回对象的地址
hash
方法 | 意义 |
---|---|
__hash__ |
内建函数hash() 调用的返回值,返回一个整数。如果定义这个方法该类的实例就可hash |
__eq__ |
对应== 操作符,判断2个对象内容是否相等,返回bool值定义了这个方法,如果不提供 __hash__ 方法,那么实例将不可hash了 |
class A:
def __init__(self, name):
self.name = name
def __repr__(self):
return self.name
def __hash__(self):
return 100
def __eq__(self, other):
return True
a = A('Tom')
b = A('Tom')
c = A('rose')
print(a == b)
print(a == c)
print(1, hash(a), id(a))
print(2, hash(b), id(b))
print(3, (a, b, c))
print(4, [a, b, c])
print(5, {a, b, c})
#结果
True
True
1 100 2223860224464
2 100 2223860224184
3 (Tom, Tom, rose)
4 [Tom, Tom, rose]
5 {Tom}
因为上例的__hash__
和__eq__
返回设置的是固定值,所以,当出现比较的时候,所有的都是一样的,而集合先调用__hash__
,结果是一样的,然后需要去重,这个时候就要调用__eq__
方法,结果返回的都是Ture,所以所有的结果都是一样的
修改
class A:
def __init__(self, name):
self.name = name
def __repr__(self):
return self.name
def __hash__(self):
return hash(self.name)
def __eq__(self, other):
return self.name==other.name
a = A('Tom')
b = A('Tom')
c = A('rose')
print(a == b)
print(a is b)
print(1, hash(a), id(a))
print(2, hash(b), id(b))
print(3, (a, b, c))
print(4, [a, b, c])
print(5, {a, b, c})
#结果
True
False
1 5640338519104148085 1394922099264
2 5640338519104148085 1394922099320
3 (Tom, Tom, rose)
4 [Tom, Tom, rose]
5 {Tom, rose}
可以看出来,hash()
调用的是__hash__
方法
去重的时候调用的是__eq__
方法
__hash__
方法只是返回一个hash值作为set的key,但是去重,还需要__eq__
来判断2个对象是否相等。hash值相等,只是hash冲突,不能说明两个对象是相等的。
因此,一般来说提供
__hash__
方法是为了作为set或者dict的key,如果去重要同时提供__eq__
方法。不可hash对象
isinstance(p1, collections.Hashable)
一定为False。去重需要提供
__eq__
方法
字典,会先调用__hash__
如果__hash__
相同,再调用__eq__
bool
方法 | 意义 |
---|---|
__bool__ |
内建函数bool() ,或者对象放在逻辑表达式的位置,调用这个函数返回布尔值。没有定义 __bool__() ,就找__len__() 返回长度,非0为真。如果 __len__() 也没有定义,那么所有实例都返回真 |
class A:
def __init__(self, name):
self.name = name
def __bool__(self):
return True
a = A(1)
b = A(0)
print(bool(a))
print(bool(1))
print(bool(b))
print(bool(0))
#结果
True
True
True
False
class A(list):
pass
a = A()
a.append(1)
b = A()
print(a)
print(bool(a))
print(b)
print(bool(b))
print('-' * 30)
#结果
[1]
True
[]
False
------------------------------
class B(list):
def __len__(self):
return 0
c = B()
c.append(1)
d = B()
print(c)
print(bool(c))
print(d)
print(bool(d))
#结果
[1]
False
[]
False
class A(list):
def __len__(self):
return 1
a = A()
a.append(1)
print(a)
if a:
print('a不为空')
if A():
print('A()不为空')
#结果
[1]
a不为空
A()不为空
上例可以看出,if 判断也是先调用__bool__
,__bool__
方法未定义就调用__len__
方法,上例中__len__
方法返回的是非0数,无论是否真为空,一直为真,所以if语句一直执行
运算符重载
operator模块提供以下的特殊方法,可以将类的实例使用下面的操作符来操作
运算符 | 特殊方法 | 含义 |
---|---|---|
<, <=, ==, >,>=, != | __lt__ , __le__ , __eq__ , __gt__ , __ge__ ,__ne__ |
比较运算符 |
+, -, *, /, %,//, **,divmod(求商和余数) | __add__ , __sub__ , __mul__ , __truediv__ ,__mod__ , __floordiv__ , __pow__ , __divmod__ |
算数运算符,移位、位运算也有对应的方法 |
+=, -=, *=,/=, %=, //=,**= | __iadd__ , __isub__ , __imul__ , __itruediv__ ,__imod__ , __ifloordiv__ , __ipow__ |
就地修改 |
class A:
def __init__(self, name, age):
self.name = name
self.age = age
a = A('a', 20)
b = A('b', 10)
print(a - b)
#结果
Traceback (most recent call last):
File "C:/Users/Administrator/Desktop/学习笔记/Python/代码/t1.py", line 9, in <module>
print(a - b)
TypeError: unsupported operand type(s) for -: 'A' and 'A'
上例中,实例无法相减,提示没有相减的方法
修改——————》添加减方法
class A:
def __init__(self, name, age):
self.name = name
self.age = age
def __sub__(self, other):
print('sub——————')
return self.age - other.age
a = A('a', 20)
b = A('b', 10)
print(a - b)
#结果
sub——————
10
如果是就地修改
class A:
def __init__(self, name, age):
self.name = name
self.age = age
def __sub__(self, other):
print('sub——————')
return self.age - other.age
a = A('a', 20)
b = A('b', 10)
print(a - b)
a-=b
print(a,type(a))
#结果
sub——————
10
sub——————
10 <class 'int'>
也是调用的__sub__
方法,但是因为就地修改,会出现直接把对象修改成int类型,所以需要重载__isub__
方法
class A:
def __init__(self, name, age):
self.name = name
self.age = age
def __sub__(self, other):
print('sub——————')
return self.age - other.age
def __isub__(self, other):
print('isub——————')
self.age -= other.age
return self
def __repr__(self):
return '< {}.{} {}>'.format(__class__.__name__, self.name, self.age)
a = A('a', 20)
b = A('b', 10)
print(a,b)
a -= b
print(a, type(a))
#结果
< A.a 20> < A.b 10>
isub——————
< A.a 10> <class '__main__.A'>
__isub__
方法定义,一般会in-place就地来修改自身
如果没有定义__isub__
方法,则会调用__sub__
练习
完成Point类设计,实现判断点相等的方法,并完成向量的加法在直角坐标系里面,定义原点为向量的起点。两个向量和与差的坐标分别等于这两个向量相应坐标的和
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __add__(self, other):
return (self.x + other.x, self.y + other.y)
def __iadd__(self, other):
self.x += other.x
self.y += other.y
return self
def __repr__(self):
return '< {}->({},{})>'.format(__class__.__name__, self.x, self.y)
a = Point(2, 3)
c = Point(2, 3)
b = Point(3, 4)
print(a, b)
print(a == b)
print(a == c)
print(a + b)
print(a)
a += b
print(a)
#结果
< Point->(2,3)> < Point->(3,4)>
False
True
(5, 7)
< Point->(2,3)>
< Point->(5,7)>
__eq__
等于可以推断不等于
__gt__
大于可以推断小于
__ge__
大于等于可以推断小于等于也就是用3个方法,就可以把所有比较解决了
容器相关方法
方法 | 意义 |
---|---|
__len__ |
内建函数len(),返回对象的长度(>=0的整数),如果把对象当做容器类型看,就如同list或者dict。 bool()函数调用的时候,如果没有 __bool__() 方法,则会看__len__() 方法是否存在,存在返回非0为真 |
__iter__ |
迭代容器时,调用,返回一个新的迭代器对象 |
__contains__ |
in 成员运算符,没有实现,就调用__iter__ 方法遍历 |
__getitem__ |
实现self[key]访问。序列对象,key接受整数为索引,或者切片。对于set和dict,key为hashable。key不存在引发KeyError异常 |
__setitem__ |
和__getitem__ 的访问类似,是设置值的方法 |
__missing__ |
字典或其子类使用__getitem__() 调用时,key不存在执行该方法 |
class A:
def __init__(self):
self.__iterm = []
# 格式化打印
def __repr__(self):
return str(self.__iterm)
# 实现添加元素
def add_iter(self, key):
return self + key
# 对+号进行重载
def __add__(self, other):
self.__iterm.append(other)
return self
# 对-号进行重载
def __sub__(self, other):
self.__iterm.remove(other)
# 通过索引获取元素
def __getitem__(self, item):
return self.__iterm[item]
# 通过索引进行修改元素
def __setitem__(self, key, value):
self.__iterm[key] = value
return self
#重载生成迭代器
def __iter__(self):
print('iter----')
# return iter(self.__iterm)
yield from self.__iterm
a = A()
print(a)
a.add_iter(1).add_iter(2)
a + 3 + 4 + 'asd'
a - 4
print(a)
print(a[1])
a[2] = 'wwww'
print(a)
print('----------------')
for i in a:
print(i)
#结果
[]
[1, 2, 3, 'asd']
2
[1, 2, 'wwww', 'asd']
----------------
iter----
1
2
wwww
asd
可调用对象
Python中一切皆对象,函数也不例外
def foo():
print(foo.__module__, foo.__name__)
foo() # 等价于
foo.__call__()
#结果
__main__ foo
__main__ foo
函数即对象,对象foo
加上()
,就是调用此函数对象的__call__()
方法
方法 | 意义 |
---|---|
__call__ |
类中定义一个该方法,实例就可以像函数一样调用 |
可调用对象:定义一个类,并实例化得到其实例,将实例像函数一样调用。
class A:
def __init__(self):
self.ans = 0
def __call__(self, *args):
self.ans = sum(args)
return self.ans
a = A()
print(a(1, 2, 3, 4))
#结果
10
练习
定义一个斐波那契数列的类,方便调用,计算第n项。
增加迭代数列的方法、返回数列长度、支持索引查找数列项的方法
class Fib:
#初始化,准备一个容器存储计算的数据
def __init__(self):
self.item = [0, 1]
#只计算容器中没有的数据,然后从容器中返回对应项
def __getitem__(self, n):
if n < 0:
raise KeyError
for i in range(len(self.item), n + 1):
self.item.append(self.item[i - 1] + self.item[i - 2])
return self.item[n]
#因为__call__方法和__getitem__方法一致
__call__=__getitem__
def __len__(self):
return len(self.item)
def __iter__(self):
yield from self.item
f = Fib()
print(f(10))
for i in f:
print(i, '++')
print(f[5])
print(len(f))
#结果
55
0 ++
1 ++
1 ++
2 ++
3 ++
5 ++
8 ++
13 ++
21 ++
34 ++
55 ++
5
11
上下文管理
文件IO操作可以对文件对象使用上下文管理,使用with...as语法。
仿照上例写一个自己的类,实现上下文管理
class A:
def __init__(self):
print('1 init------------------')
def __enter__(self):
print('2 enter--------------')
def __exit__(self, exc_type, exc_val, exc_tb):
print('5 exit------------------')
with A() as f:
print('3 开始--------------')
print('4 结束--------------')
print('6 end---------------')
#结果
1 init------------------
2 enter--------------
3 开始--------------
4 结束--------------
5 exit------------------
6 end---------------
加入中断
class A:
def __init__(self):
print('1 init------------------')
def __enter__(self):
print('2 enter--------------')
def __exit__(self, exc_type, exc_val, exc_tb):
print('5 exit------------------')
with A() as f:
print('3 开始--------------')
exit(100)
print('4 结束--------------')
print('6 end---------------')
#结果
1 init------------------
2 enter--------------
3 开始--------------
5 exit------------------
当一个对象同时实现了__enter__()
和__exit__()
方法,它就属于上下文管理的对象
方法 | 意义 |
---|---|
__enter__ |
进入与此对象相关的上下文。如果存在该方法,with语法会把该方法的返回值作为绑定到as子句中指定的变量上 |
__exit__ |
退出与此对象相关的上下文 |
实例化对象的时候,并不会调用enter,进入with语句块调用__enter__
方法,然后执行语句体,最后离开with语句块的时候,调用__exit__
方法。
with可以开启一个上下文运行环境,在执行前做一些准备工作,执行后做一些收尾工作。
注意,with并不开启一个新的作用域
with语句
class A:
def __init__(self):
print('1 init------------------')
def __enter__(self):
print('2 enter--------------')
def __exit__(self, exc_type, exc_val, exc_tb):
print('5 exit------------------')
a = A()
with a as f:
print('a=',a)
print('f=',f)
print(a is f, '+++')
print(a == f)
print('6 end---------------')
#结果
1 init------------------
2 enter--------------
a= <__main__.A object at 0x0000025801751208>
f= None
False +++
False
5 exit------------------
6 end---------------
上面可以看出a是对象的地址,但是f却是None
是因为,f是__enter__
方法上的返回值
因为没有返回值,所以为None
在__enter__
中添加return self
就可以了
with语法,会调用with后的对象的
__enter__
方法,如果有as,则将该方法的返回值赋给as子句的变量。上例,可以等价为
f = a.__enter__()
方法的参数
__enter__
方法没有其他参数。
__exit__
方法有3个参数:
__exit__(self, exc_type, exc_value, exc_tb)
这三个参数都与异常有关。
如果该上下文退出时没有异常,这3个参数都为None。
如果有异常,参数意义如下
exc_type
,异常类型
exc_value
,异常的值
exc_tb
,异常的追踪信息
__exit__
方法返回一个等效True的值,则压制异常;否则,继续抛出异常
class A:
def __init__(self):
print('1 init------------------')
def __enter__(self):
print('2 enter--------------')
def __exit__(self, exc_type, exc_val, exc_tb):
print('5 exit------------------')
print('异常类型', exc_type)
print('异常值', exc_val)
print('异常的追踪', exc_tb)
a = A()
with a as f:
print('#########')
1 / 0
print('6 end---------------')
#结果
1 init------------------
2 enter--------------
#########
5 exit------------------
异常类型 <class 'ZeroDivisionError'>
异常值 division by zero
异常的追踪 <traceback object at 0x0000017BE50C52C8>
Traceback (most recent call last):
File "C:/Users/Administrator/Desktop/学习笔记/Python/代码/t1.py", line 18, in <module>
1/0
ZeroDivisionError: division by zero
练习
为加法函数计时方法
1、使用装饰器显示该函数的执行时长方法
2、使用上下文管理方法来显示该函数的执行时长
装饰器解决
import datetime, time
from functools import wraps
def get_time(fn):
@wraps(fn)
def wrapper(*args,**kwargs):
star = datetime.datetime.now()
c = fn(*args,**kwargs)
t = datetime.datetime.now() - star
print(t)
return c
return wrapper
@get_time
def add(a, b):
time.sleep(2)
return a + b
print(add(1, 2))
#结果
0:00:02.004639
3
用上下文管理来解决
import datetime, time
def add(a, b):
time.sleep(2)
return a + b
class Get_time:
def __init__(self, fn):
self.fn = fn
def __enter__(self):
self.star = datetime.datetime.now()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
t = datetime.datetime.now() - self.star
print('耗时', t)
def __call__(self, *args):
return self.fn(*args)
with Get_time(add) as f:
print(f(1, 2))
用类作为装饰器
import datetime, time
class Get_time:
"""
这里是Get_tim
"""
def __init__(self, fn):
self.fn = fn
self.__doc__=self.fn.__doc__
self.__name__=self.fn.__name__
def __call__(self, *args):
stat=datetime.datetime.now()
ans=self.fn(*args)
t=datetime.datetime.now()-stat
print('耗时', t)
return ans
@Get_time
def add(a, b):
"""
这里是add
"""
time.sleep(2)
return a + b
print(add(1, 2))
print(add.__doc__)
print(add.__name__)
#结果
耗时 0:00:02.012385
3
这里是add
add
使用functools
中的update_wrapper
,wraps
import datetime, time
from functools import update_wrapper, wraps
class Get_time:
"""
这里是Get_tim
"""
def __init__(self, fn):
self.fn = fn
# update_wrapper(self, fn)
wraps(fn)(self)
def __call__(self, *args):
stat = datetime.datetime.now()
ans = self.fn(*args)
t = datetime.datetime.now() - stat
print('耗时', t)
return ans
@Get_time
def add(a, b):
"""
这里是add
"""
time.sleep(2)
return a + b
print(add(1, 2))
print(add.__doc__)
print(add.__name__)
#结果
耗时 0:00:02.004670
3
这里是add
add
update_wrapper(self, fn)
是先写装饰器,再写被装饰函数
wraps(fn)(self)
是先写被装饰函数,再写装饰器
上下文应用场景
-
增强功能
在代码执行的前后增加代码,以增强其功能。类似装饰器的功能。
-
资源管理
打开了资源需要关闭,例如文件对象、网络连接、数据库连接等
-
权限验证
在执行代码之前,做权限的验证,在
__enter__
中处理
contextlib.contextmanager
contextlib.contextmanager
它是一个装饰器实现上下文管理,装饰一个函数,而不用像类一样实现__enter__
和__exit__
方法。
对下面的函数有要求:必须有yield,也就是这个函数必须返回一个生成器,且只有yield一个值。
也就是这个装饰器接收一个生成器对象作为参数
from contextlib import contextmanager
@contextmanager
def A():
print('1 enter-------------')
try:
yield 111111
print('4 +++++++++++++++')
finally:
print('5 exit------------------')
with A() as f:
print('2 star----------------')
print('3 f=',f)
#结果
1 enter-------------
2 star----------------
3 f= 111111
4 +++++++++++++++
5 exit------------------
f接收yield语句的返回值
如果下面with语句中有异常,是不会执行print('5 exit------------------')
所以,需要利用try............finally..............
来实现__exit__
的功能
再写对add语句计时
from contextlib import contextmanager
from datetime import datetime
from time import sleep
@contextmanager
def A():
star = datetime.now()
try:
yield
print('4 +++++++++++++++')
finally:
print(5, datetime.now() - star)
def add(x, y):
print('2 add--------')
sleep(2)
return x + y
with A() as f:
print('1 star----------------')
print(3, add(1, 2))
#结果
1 star----------------
2 add--------
3 3
4 +++++++++++++++
5 0:00:02.002123
或者,通过传参的方式实现
from contextlib import contextmanager
from datetime import datetime
from time import sleep
@contextmanager
def A(fn):
star = datetime.now()
try:
yield fn
print('4 +++++++++++++++')
finally:
print(5, datetime.now() - star)
def add(x, y):
print('2 add--------')
sleep(2)
return x + y
with A(add) as f:
print('1 star----------------')
print(3, f(1,2))
#结果
1 star----------------
2 add--------
3 3
4 +++++++++++++++
5 0:00:02.000568
当yield发生处为生成器函数增加了上下文管理。这是为函数增加上下文机制的方式。
把yield之前的当做__enter__
方法执行
把yield之后的当做__exit__
方法执行
把yield的值作为__enter__
的返回值
总结
如果业务逻辑简单可以使用函数加contextlib.contextmanager装饰器方式,如果业务复杂,用类的方式加__enter__
和__exit__
方法方便
反射
反射,reflection,指的是运行时获取类型定义信息。
一个对象能够在运行时,像照镜子一样,反射出其类型信息。
简单说,在Python中,能够通过一个对象,找出其type、class、attribute或method的能力,称为反射或者自省。
具有反射能力的函数有 type()
、isinstance()
、callable()
、dir()
、getattr()
等
反射相关的函数和方法
内建函数 | 意义 |
---|---|
getattr(object,name[, default]) |
通过name 返回object的属性值。当属性不存在,将使用 default 返回如果没有 default ,则抛出AttributeError 。name 必须为字符串 |
setattr(object,name, value) |
object 的属性存在,则覆盖,不存在,新增 |
hasattr(object,name) |
判断对象是否有这个名字的属性,name 必须为字符串 |
class A:
def __init__(self, x, y):
self.x = x
self.y = y
def show(self):
print('<x={} y={}>'.format(self.x, self.y))
a=A(3,4)
print(getattr(a, 'x'))
print(getattr(a, 'z','没有z'))
print(getattr(a,'__dict__'))
setattr(a,'y',10)
setattr(a,'z',100)
print(getattr(a,'__dict__'))
print(hasattr(a, 'show'))
getattr(a,'show','没有show属性')()
#结果
3
没有z
{'x': 3, 'y': 4}
{'x': 3, 'y': 10, 'z': 100}
True
<x=3 y=10>
使用反射为对象添加方法
class A:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return '< A-->({},{}) >'.format(self.x, self.y)
a = A(3, 4)
b = A(5, 6)
#对类添加方法
if not hasattr(A, 'add'):
setattr(A, 'add', lambda self, other: A(self.x + other.x, self.y + other.y))
print(a.add(b))
#对实例添加方法
if not hasattr(a, 'sub'):
setattr(a, 'sub', lambda m, n: A(m.x - n.x, m.y - n.y))
print(a.sub(a, b))
#结果
< A-->(8,10) >
< A-->(-2,-2) >
class C:
def fn(self,a):
if a:
print('None是非空')
else:
print('None是空')
getattr(C(), "fn")(None)
反射相关的魔术方法
__getattr__()
、__setattr__()
、__delattr__()
这三个魔术方法,分别测试这三个方法
__getattr__()
class A:
def __init__(self, x, y):
self.x = x
self.y = y
def __getattr__(self, item):
print('__getattr__++++++++++++', item)
return item
def __repr__(self):
return '< A-->({},{}) >'.format(self.x, self.y)
a = A(3, 4)
#无论是用.获取__dict___中的属性,还是用getattr获取不存在的属性,都是在调用__getattr__方法
print(a.w)
print(getattr(a, 'z', 123))
#结果
__getattr__++++++++++++ w
w
__getattr__++++++++++++ z
z
无论是用.
获取__dict__
中的属性,还是用getattr获取不存在的属性,都是在调用__getattr__
方法
查找属性顺序为:instance.__dict__
--> instance.__class__.__dict__
--> 继承的祖先类(直到object)的__dict__
---找不到--> 调用__getattr__()
__setattr__()
class B:
n=222
class A(B):
m=111
def __init__(self, x, y):
print('init-------------star')
self.x = x
self.y = y
print('init-------------end')
def __setattr__(self, key, value):
print(f'__setattr__----------{key}:{value}')
def __getattr__(self, item):
print('没有{}属性!'.format(item))
def __repr__(self):
return '< A-->({},{}) >'.format(self.x, self.y)
a = A(3, 4)
print('*'*30)
print(a.__dict__)
print(1,a.x)
print(2,a.y)
print(3,a.m)
print(4,a.n)
#结果
init-------------star
__setattr__----------x:3
__setattr__----------y:4
init-------------end
******************************
{}
没有x属性!
1 None
没有y属性!
2 None
3 111
4 222
上例可以看出,创建实例,调用初始化方法__init__
时,会调用__setattr__
方法,当__setattr__
方法没有对属性进行操作,a.__dict__
字典为空,也就无法查到实例的属性,只能查到继承自类的属性
修改
class B:
n = 222
class A(B):
m = 111
def __init__(self, x, y):
print('init-------------star')
self.x = x
self.y = y
print('init-------------end')
def __setattr__(self, key, value):
print(f'__setattr__----------{key}:{value}')
self.__dict__[key] = value
# super().__setattr__(key,value)
def __getattr__(self, item):
print('没有{}属性!'.format(item))
def __repr__(self):
return '< A-->({},{}) >'.format(self.x, self.y)
a = A(3, 4)
print('*' * 30)
print(a.__dict__)
print(1, a.x)
print(2, a.y)
print(3, a.m)
print(4, a.n)
print('='*30)
setattr(a,'z',1000)
print(a.__dict__)
#结果
init-------------star
__setattr__----------x:3
__setattr__----------y:4
init-------------end
******************************
{'x': 3, 'y': 4}
1 3
2 4
3 111
4 222
==============================
__setattr__----------z:1000
{'x': 3, 'y': 4, 'z': 1000}
从上例可以看出
__setattr__
方法可以写成
直接给__dict__
注入kv对:self.__dict__[key] = value
或者,交给父类处理super().__setattr__(key,value)
setattr()
内建函数也是调用的__setattr__
方法
__delattr__()
class A:
m = 111
n = 222
def __init__(self, x, y):
self.x = x
self.y = y
def __delattr__(self, item):
print('删除属性', item)
a = A(3, 4)
print('*' * 30)
del A.m
print('+' * 30)
del a.n
del a.x
#结果
******************************
++++++++++++++++++++++++++++++
删除属性 n
删除属性 x
可以看出,只有删除实例的属性时才会调用__delattr__
方法
可以阻止通过实例来删除属性的操作。但是通过类依然可以删除类属性。
__getattribute__
class A:
m = 111
n = 222
def __init__(self, x, y):
self.x = x
self.y = y
def __getattribute__(self, item):
print('__getattribute__-----------', item)
return '__getattribute__******'
a = A(3, 4)
print(a.x)
print(a.__dict__)
print('=' * 30)
print(object.__getattribute__(a, '__dict__'))
#结果
__getattribute__----------- x
__getattribute__******
__getattribute__----------- __dict__
__getattribute__******
==============================
{'x': 3, 'y': 4}
可以看出,实例对象的字典时存在的,但是因为定义了__getattribute__
方法,被__getattribute__
完全拦截了,导致任何属性返回的都是__getattribute__
方法返回的结果
实例的所有的属性访问,第一个都会调用__getattribute__
方法,它阻止了属性的查找,该方法应该返回(计算后的)值或者抛出一个AttributeError
异常
它的return
值将作为属性查找的结果。
如果抛出AttributeError
异常,则会直接调用__getattr__
方法,因为表示属性没有找到
可以改写成
class A:
m = 111
n = 222
def __init__(self, x, y):
self.x = x
self.y = y
def __getattribute__(self, item):
print('__getattribute__-----------', item)
# return object.__getattribute__(self,item)
return super().__getattribute__(item)
a = A(3, 4)
print(a.x)
print(a.__dict__)
print('=' * 30)
print(object.__getattribute__(a, '__dict__'))
#结果
__getattribute__----------- x
3
__getattribute__----------- __dict__
{'x': 3, 'y': 4}
==============================
{'x': 3, 'y': 4}
__getattribute__
方法中为了避免在该方法中无限的递归,它的实现应该永远调用基类的同名方法以访问需要的任何属性,例如object.__getattribute__(self, name)
。
注意,除非你明确地知道__getattribute__
方法用来做什么,否则不要使用它
总结
魔术方法 | 意义 |
---|---|
__getattr__() |
当通过搜索实例、实例的类及祖先类查不到属性,就会调用此方法 |
__setattr__() |
通过. 访问实例属性,进行增加、修改都要调用它 |
__delattr__() |
当通过实例来删除属性时调用此方法 |
__getattribute__ |
实例所有的属性调用都从这个方法开始 |
属性查找顺序
实例调用__getattribute__()
--->instance.__dict__
---> instance.__class__.__dict__
---> 继承的祖先类(直到object
)的__dict__
---> 调用__getattr__()
描述器 Descriptors
描述器的表现
用到3个魔术方法:__get__()
、__set__()
、__delete__()
方法签名如下:
object.__get__(self, instance, owner)
object.__set__(self, instance, value)
object.__delete__(self, instance)
self
指代当前实例,调用者
instance
是owner
的实例
owner
是属性的所属的类
class A:
def __init__(self):
print('A init----------------')
self.x = 'x1'
def __get__(self, instance, owner):
print('get-------', self, instance, owner)
return self
def __repr__(self):
return '< A 的实例 >'
class B:
y = A()
def __init__(self):
print('B init ----------------')
self.z = A()
def __repr__(self):
return '< B 的实例 >'
print('-' * 30)
print(B.y)
print(B.y.x)
print('=' * 30)
b = B()
print(b.y)
print(b.z)
#结果
A init----------------
------------------------------
get------- < A 的实例 > None <class '__main__.B'>
< A 的实例 >
get------- < A 的实例 > None <class '__main__.B'>
x1
==============================
B init ----------------
A init----------------
get------- < A 的实例 > < B 的实例 > <class '__main__.B'>
< A 的实例 >
< A 的实例 >
可以看出,只有作为类属性时,才会调用__get__
方法,作为实例的属性,不会调用
__get__
方法是当其他实例调用本实例时调用的
描述器定义
Python中,一个类实现了__get__
、__set__
、__delete__
三个方法中的任何一个方法,就是描述器。实现这三个中的某些方法,就支持了描述器协议。
仅实现了__get__
,就是非数据描述符 non-data descriptor
实现了__get__
、__set__
就是数据描述符 data descriptor
如果一个类的类属性设置为描述器实例,那么它被称为owner属主。
当该类的该类属性被查找、设置、删除时,就会调用描述器相应的方法
仅实现__get__
class A:
def __init__(self):
print('A init----------------')
self.x = 'x1'
def __get__(self, instance, owner):
print('get-------', self, instance, owner)
return self
def __repr__(self):
return '< A 的实例 >'
class B:
y = A()
def __init__(self):
print('B init ----------------')
self.y = 'abc'
print('+++')
def __repr__(self):
return '< B 的实例 >'
print('=' * 30)
b = B()
print(b.y)
#结果
A init----------------
==============================
B init ----------------
+++
abc
b.y
访问的是初始化中的实例属性y
是非数据描述符
同时实现了__get__
、__set__
class A:
def __init__(self):
print('A init----------------')
self.x = 'x1'
def __get__(self, instance, owner):
print('get-------', self, instance, owner)
return self
def __set__(self, instance, value):
print('set-------', self, instance, value)
self.data=value
def __repr__(self):
return '< A 的实例 >'
class B:
y = A()
def __init__(self):
print('B init ----------------')
self.y = 'abc'
print('+++++++++++++++++')
def __repr__(self):
return '< B 的实例 >'
print('=' * 30)
b = B()
print(b.y)
print('*'*30)
print(b.y.data)
#结果
A init----------------
==============================
B init ----------------
set------- < A 的实例 > < B 的实例 > abc
+++++++++++++++++
get------- < A 的实例 > < B 的实例 > <class '__main__.B'>
< A 的实例 >
******************************
get------- < A 的实例 > < B 的实例 > <class '__main__.B'>
abc
执行顺序是
因为B类属性中有y = A()
,所以先执行A实例的初始化
再执行B实例对象的初始化,由于A类中同时实现了__get__
、__set__
,B实例对象的__init__
中又出现了B类属性相同的属性y
,所以,成为了数据描述符,需要要调用__set__
第三步执行A类中的 __set__
第四步,b.y
,调用的是A类的__get__
方法,返回值是A的实例对象
要想获取B实例属性的y,可以在A类中的 __set__
中进行存储,然后通过b.y.data
调用
属性查找的优先级
数据描述器>__dict__
>非数据描述器
Python中的描述器
Python的方法(包括staticmethod()和classmethod())都实现为非数据描述器。因此,实例可以重新定义和覆盖方法。这允许单个实例获取与同一类的其他实例不同的行为。
property()函数实现为一个数据描述器。因此,实例不能覆盖属性的行为
仿写类方法classmethod()
和静态方法staticmethod()
from functools import partial
class Classmethod:
def __init__(self, fn):
self.fn = fn
def __get__(self, instance, owner):
print('C-get-----',self, instance, owner)
return partial(self.fn,owner)
def __repr__(self):
return ('<Classmethod------>')
class Statistics:
def __init__(self,fn):
self.fn=fn
def __get__(self, instance, owner):
print('S-get----------',self,instance,owner)
return self.fn
class A:
@Classmethod
def test(cls,a):
print('test------',cls,a)
@Statistics
def foo():
print('foo---------')
A.test(1)
A().test(1)
A.foo()
A().foo()
#结果
C-get----- <Classmethod------> None <class '__main__.A'>
test------ <class '__main__.A'> 1
C-get----- <Classmethod------> <__main__.A object at 0x000001964D3612E8> <class '__main__.A'>
test------ <class '__main__.A'> 1
S-get---------- <__main__.Statistics object at 0x000001964D3612B0> None <class '__main__.A'>
foo---------
S-get---------- <__main__.Statistics object at 0x000001964D3612B0> <__main__.A object at 0x000001964D3616D8> <class '__main__.A'>
foo---------
练习
对数据类型进行校验
class Type_chack:
def __init__(self, typ):
self.typ = typ
def __get__(self, instance, owner):
if instance:
return instance.__dict__[self.pname]
else:
raise TypeError('不可用类直接访问!')
def __set__(self, instance, value):
if instance and isinstance(value, self.typ):
instance.__dict__[self.pname] = value #符合条件,就把对应属性的对应值塞到对象的字典中
else:
raise TypeError
def __set_name__(self, owner, name): #获取属性名字
self.pname = name
class A:
name = Type_chack(str)
age = Type_chack(int)
def __init__(self, name, age):
self.name = name
self.age = age
a = A('a', 12)
print(a.age)
print(a.__dict__)
print('=' * 30)
b = A('a', '12')
#结果
12
{'name': 'a', 'age': 12}
==============================
Traceback (most recent call last):
File "C:/Users/Administrator/Desktop/学习笔记/Python/代码/t1.py", line 31, in <module>
b = A('a', '12')
File "C:/Users/Administrator/Desktop/学习笔记/Python/代码/t1.py", line 24, in __init__
self.age = age
File "C:/Users/Administrator/Desktop/学习笔记/Python/代码/t1.py", line 12, in __set__
raise TypeError
TypeError
利用inspect做装饰器来生成前期的对象
import inspect
class Type_chack:
def __init__(self, name, typ):
self.typ = typ
self.pname = name
def __get__(self, instance, owner):
return instance.__dict__[self.pname]
def __set__(self, instance, value):
if instance and isinstance(value, self.typ):
instance.__dict__[self.pname] = value
else:
raise TypeError
def creat_object(cls):
dict_list = inspect.signature(cls).parameters
for k, v in dict_list.items():
if v.annotation is not v.empty:
setattr(cls, k, Type_chack(k, v.annotation))
return cls
@creat_object
class A: #A=creat_object(A)
def __init__(self, name: str, age: int):
self.name = name
self.age = age
print('----------')
a = A('a', 15)
print(a.age)
print(a.__dict__)
print('=' * 30)
b = A('a', '16')
print(b, '~~~~~~~~~~~~')
#结果
----------
15
{'name': 'a', 'age': 15}
==============================
Traceback (most recent call last):
File "C:/Users/Administrator/Desktop/学习笔记/Python/代码/t1.py", line 39, in <module>
b = A('a', '16')
File "C:/Users/Administrator/Desktop/学习笔记/Python/代码/t1.py", line 31, in __init__
self.age = age
File "C:/Users/Administrator/Desktop/学习笔记/Python/代码/t1.py", line 16, in __set__
raise TypeError
TypeError
用类来做装饰器
import inspect
class Type_chack:
def __init__(self, name, typ):
self.typ = typ
self.pname = name
def __get__(self, instance, owner):
return instance.__dict__[self.pname]
def __set__(self, instance, value):
if instance and isinstance(value, self.typ):
instance.__dict__[self.pname] = value
else:
raise TypeError
# def creat_object(cls):
# dict_list = inspect.signature(cls).parameters
# for k, v in dict_list.items():
# if v.annotation is not v.empty:
# setattr(cls, k, Type_chack(k, v.annotation))
# return cls
class creat_object:
def __init__(self, fn):
self.fn = fn
dict_list = inspect.signature(self.fn).parameters
for k, v in dict_list.items():
if v.annotation is not v.empty:
setattr(self.fn, k, Type_chack(k, v.annotation))
def __call__(self, *args):
return self.fn(*args)
@creat_object
class A: # A=creat_object(A)
def __init__(self, name: str, age: int):
self.name = name
self.age = age
print('----------')
a = A('a', 15)
print(a)
print(a.age)
print(a.__dict__)
print('=' * 30)
b = A('a', '16')
print(b, '~~~~~~~~~~~~')
#结果
Traceback (most recent call last):
File "C:/Users/Administrator/Desktop/学习笔记/Python/代码/t1.py", line 50, in <module>
b = A('a', '16')
File "C:/Users/Administrator/Desktop/学习笔记/Python/代码/t1.py", line 34, in __call__
return self.fn(*args)
File "C:/Users/Administrator/Desktop/学习笔记/Python/代码/t1.py", line 41, in __init__
self.age = age
File "C:/Users/Administrator/Desktop/学习笔记/Python/代码/t1.py", line 16, in __set__
raise TypeError
TypeError
----------
<__main__.A object at 0x0000025F0F5612B0>
15
{'name': 'a', 'age': 15}
==============================
综合,写出双向链表
具有+、-、pop、insert、正反向打印、len、iter
class Data_lib:
def __init__(self, item, prev=None, next=None):
self.prev = prev
self.item = item
self.next = next
def __repr__(self):
return '{:<4}<=={:^4}==>{:>4}'.format(self.prev.item if self.prev else 'None', self.item,
self.next.item if self.next else 'None')
class Link_list:
def __init__(self):
self.head = None
self.tail = None
self.up = None
self.__size = 0
def __add__(self, key):
d = Data_lib(key)
if self.head is None:
self.head = d
else:
self.up = self.tail
d.prev = self.up
self.tail.next = d
self.tail = d
self.__size += 1
return self
def insert(self, index, value):
if index < -len(self):
index = 0
if index > len(self):
self + value
return self
elif index >= 0:
v = self[index]
d = Data_lib(value, v.prev, v)
if v.prev == None:
self.head = d
else:
v.prev.next = d
v.prev = d
self.__size += 0
return self
back = self[index]
d = Data_lib(value, back, back.next)
if back.next == None:
self.tail = d
else:
back.next.prev = d
back.next = d
self.__size += 1
return self
def __sub__(self, other):
for v in self:
if other == v.item:
if v.next == None:
if v.prev == None:
self.tail = None
self.head = None
return self
else:
self.tail = v.prev
else:
v.next.prev = v.prev
if v.prev == None:
self.head = v.next
else:
v.prev.next = v.next
self.__size -= 1
return self
else:
raise ValueError('没有 {}'.format(other))
def pop(self, index=-1):
back=self[index]
if back.next == None:
self.tail = back.prev
else:
back.next.prev = back.prev
if back.prev == None:
self.head = back.next
else:
back.prev.next = back.next
self.__size -= 1
return back.item
def __getitem__(self, item):
if item >= 0 and self.head != None:
for i, v in enumerate(self):
if i == item:
return v
else:
raise IndexError('超界!')
elif item < 0 and self.head != None:
back = self.tail
a = abs(item)
i = 1
while True:
if i == a:
return back
else:
i += 1
back = back.prev
if back == None:
raise IndexError('没有索引 {}'.format(item))
else:
raise IndexError('超界!')
def __setitem__(self, key, value):
self[key].item = value
def __len__(self):
return self.__size
def iter_node(self, reverce=False):
go = self.head if not reverce else self.tail
while go:
yield go
go = go.next if not reverce else go.prev
__iter__ = iter_node
l = Link_list()
def print_list(reverce=False):
if l.head == None:
print('head=', l.head)
print('tail=', l.tail)
else:
print('head=', l.head.item)
for i in l.iter_node(reverce):
print(i)
print('tail=', l.tail.item)
print('#' * 30)
l + 5 + 6 + 7 + 8
print_list()
print(l[3])
l[-4] = 100
print_list()
print(len(l))
l.insert(5, 1000)
print_list()
print_list(True)
print(l.pop())
print_list()
#结果
head= 5
None<== 5 ==> 6
5 <== 6 ==> 7
6 <== 7 ==> 8
7 <== 8 ==>None
tail= 8
##############################
7 <== 8 ==>None
head= 100
None<==100 ==> 6
100 <== 6 ==> 7
6 <== 7 ==> 8
7 <== 8 ==>None
tail= 8
##############################
4
head= 100
None<==100 ==> 6
100 <== 6 ==> 7
6 <== 7 ==> 8
7 <== 8 ==>1000
8 <==1000==>None
tail= 1000
##############################
head= 100
8 <==1000==>None
7 <== 8 ==>1000
6 <== 7 ==> 8
100 <== 6 ==> 7
None<==100 ==> 6
tail= 1000
##############################
1000
head= 100
None<==100 ==> 6
100 <== 6 ==> 7
6 <== 7 ==> 8
7 <== 8 ==>None
tail= 8
##############################
仿写property装饰器
class Property:
def __init__(self, fget, fset=None):
# print('Property-----init')
self.fget = fget
self.fset = fset
def __get__(self, instance, owner):
# print('get--')
if instance:
return self.fget(instance)
else:
raise AttributeError('不能修改!')
def __set__(self, instance, value):
# print(instance, value, 'set----', self.fset)
if instance:
self.fset(instance, value)
else:
raise AttributeError('只能用对象访问!')
def setter(self, fn):
self.fset = fn
return self
class A:
def __init__(self, x):
self.__x = x
@Property
def x(self): # x=Property(x)
return self.__x
@x.setter
def x(self, value):
self.__x = value
a = A(12)
print(a.x) # 12
a.x = 16
print(a.x)
#结果
12
16
未实现和未实现异常
NotImplemented
是个值,单值,是NotImplementedType
的实例,必须通过return
返回,类似return None
,这个表示功能未实现(无能为力)!
NotImplementedError
是类型,是异常类,返回type
,是通过raise
来弹出
运算符重载中的反向方法
运算符重载的方法,例如__add__
和__iadd__
,这里还有个__radd__
方法
例如1+'a'
,前面的1是int类型,当加上字符串类型时,无法解决,返回NotImplementedType
,就会调用字符串的__radd__
方法
class A:
def __init__(self, x):
self.x = x
def __add__(self, other):
print('A add------')
def __radd__(self, other):
print('a radd----')
class B:
def __init__(self, x):
self.x = x
def __add__(self, other):
print('b add ----')
return NotImplemented
a = A(2)
b = B(2)
b + a
#结果
b add ----
a radd----
上例中
b+a
执行顺序
先在b实例中找__add__
方法,看能否执行b.__add__(a)
,然而,返回了NotImplementedType
,b表示未实现功能
接着转而问a实例有没有__radd__
方法,实例a有__radd__
方法
转而调用a的__radd__
,执行a.__radd__(b)
如果依然无法解决,就会报TypeError
,没有相加的功能
实现能自由度很高的加法
class A:
def __init__(self, x):
self.x = x
def __add__(self, other):
if isinstance(other, type(self)):
return self.x + other.x
elif isinstance(other, type(self.x)):
return self.x + other
else:
return NotImplemented
def __iadd__(self, other):
if isinstance(other, type(self)):
self.x = self.x + other.x
return self
elif isinstance(other, type(self.x)):
self.x = self.x + other
return self
else:
return NotImplemented
# __radd__ = __add__ #因为两个方法的重复度很高,可以直接写成相等
def __radd__(self, other):
if isinstance(other,type(self)):
return self.x+other.x
elif isinstance(other,type(self.x)):
return self.x+other
else:
raise TypeError('unsupported operand type(s) for +: {} and {}'.format(type(self).__name__,type(other).__name__))
a = A(1)
print(a + 1)
print(1 + a)
print('b' + a)
#结果
Traceback (most recent call last):
File "C:/Users/Administrator/Desktop/学习笔记/Python/代码/t1.py", line 36, in <module>
print('b' + a)
File "C:/Users/Administrator/Desktop/学习笔记/Python/代码/t1.py", line 30, in __radd__
raise TypeError('unsupported operand type(s) for +: {} and {}'.format(type(self).__name__,type(other).__name__))
TypeError: unsupported operand type(s) for +: A and str
2
2
可迭代对象
可迭代对象必须有__iter__
方法
class A:
def __init__(self):
self.x = [10, 20, 30, 40]
def __iter__(self):
yield from self.x
def __reversed__(self):
yield from reversed(self.x)
a = A()
print([i for i in a])
print([i for i in reversed(a)])
#结果
[10, 20, 30, 40]
[40, 30, 20, 10]
如果需要实现反向迭代,就需要实现__reversed__
方法
迭代器
生成器对象必须实现__next__
方法,也应该实现__iter__
方法
class A:
def __init__(self):
self.x = -1
def __iter__(self):
return self
def __next__(self):
self.x += 1
if self.x>10:
raise StopIteration
return self.x
a = A()
print(next(a))
print(next(a))
print(next(a))
print('='*30)
for i in a:
print(i)
print('-'*30)
print(next(a,'end--'))
#结果
0
1
2
==============================
3
4
5
6
7
8
9
10
------------------------------
end--
def __iter__(self):
return self
之所以直接return self
,是因为,下面已经实现了__next__
方法,已经是迭代器了,所以可以直接返回self