文章目录
魔术方法
class Rectangle:
def __init__(self,length,width):
self.length = length
self.width = width
def __add__(self, other):
add_length = self.length + other.length
add_width = self.width + other.width
return add_length,add_width
a = Rectangle(3,4)
b = Rectangle(5,6)
print(a.__add__(b)) #(8, 10)
print(a + b) #(8, 10)
魔术方法之运算符方法(应用不多)
__add__(self,other) #x+y
__sub__(self,other) #x-y
__mul__(self,other) #x*y
__mod__(self,other) #x%y
__iadd__(self,other) #x+=y
__isub__(self,other) #x-=y
__radd__(self,other) #y+x
__rsub__(self,other) #y-x
__imul__(self,other) #x*=y
__imod__(self,other) #x%=y
str和repr原理
- 在Python中,str和repr方法在处理对象的时候,分别调用的是对象的
__str__
和__repr__
方法 - print也是如此,调用str函数来处理输出的对象,如果对象没有定义
__str__
方法,则调用repr处理
__call__方法
- 正常情况下,实例是不能像函数一样被调用的,要想实例能够被调用,就需要定义
__call__
方法
__del__方法
class Del(object):
def __init__(self):
print('开始运行')
def __del__(self):
print('被调用')
a = Del()
b = Del()
c = Del()
案例
class Rectangle:
def __init__(self,length,width): #这样定义,实例化时要加两个实参参数Rectangle(length,width)
#def __init__(self) #这样定义,实例化时类名后面不加参数Rectangle(),实例化后在进行赋值
self.length = length
self.width = width
def area(self):
areas = self.length * self.width
return areas
def __str__(self): #__str__方法中用return,不用print
return "length is %s,width is %s"%(self.length,self.width)
def __repr__(self):
print("area is %s"%self.area())
def __call__(self):
print("call方法被调用")
a = Rectangle(20,10)
b = Rectangle(20,10)
a.__str__()
b.__repr__()
a.__call__() #length is 20,width is 10
area is 200
call方法被调用
当使用print输出对象的时候,只要自己定义了__str__
方法,那么就会打印从在这个方法中return的数据
其他魔术方法
__class__
:查看类名__base__
:查看继承的父类__bases__
:查看继承的全部父类__dict__
:查看全部属性,返回属性和属性值键值对形式__doc__
:查看对象文档,即类中的注释(用引号注释的部分)__dir__
:查看全部属性和方法
练习:
class Employee:
empCount = 0
def __int__(self,name,salary):
self.name = name
self.salary = salary
empCount += 1
def displayCount(self): #此函数可省略
print("Total Employee %d"%Employee.empCount)
def displayEmployee(self):
print("Name:",self.name,",Salary:",self.salary)
emp1 = Employee(lilei,2999)
emp2 = Employee(hanmeimei,3000)
emp1.displayEmployee()
emp2.displayEmployee()
print("Total Employee %d"%Employee.empCount)
#Name: Zara ,Salary: 2000
Name: Manni ,Salary: 5000
Total Employee 2
__new__方法
- 在类对象构造时调用的方法,也是python解释器调用
- 必须有参数cls
- 必须有返回值,返回当前类的对象
- 例1
class User(object):
def __init__(self,username,password):
self.username = username
self.password = password
print('对象构建好,由解释器自动回调的方法,对象初始化,成员方法,代表调用的当前对象本身')
def __new__(cls,username,password): #实际是静态方法,构建对象的方法,当对象构建时,由解释器自动回调的方法,该方法必须返回当前类的对象
print('User类的对象开始构建')
u = User('jingyi','520') #User类的对象开始构建
print(u) #None 因为__new__没有返回,实际没构建对象
class CapStr(str):
"""
__new__ 是第一个被自动调用的方法
__new__ 很少需要重写,如果继承一个不可变类型(比如 string),又想要修改,就可以重写
"""
def __new__(cls,string):
string = string.upper()
return str.__new__(cls, string)
# return super().__new__(cls, string)
a = CapStr('HhaxiHaXi') #HHAXIHAXI
- 例2
class User(object):
def __init__(self,username,password):
self.username = username
self.password = password
#不能用return,要用就用print
print('对象构建好,由解释器自动回调的方法,对象初始化,成员方法,代表调用的当前对象本身')
#先执行new方法,在执行init方法
def __new__(cls,username,password): #当对象构建时,由解释器自动回调的方法,该方法必须返回当前类的对象
print('User类的对象开始构建')
return object.__new__(cls) #返回cls所代表类的方法 #cls表示当前类
def __str__(self): #打印类return的对象时起作用,必须要有return
return '用户名:%s,密码:%s' % (self.username,self.password)
u = User('jingyi','520') #User类的对象开始构建
# 对象构建好,由解释器自动回调的方法,对象初始化,成员方法,代表调用的当前对象本身
print(u) # <__main__.User object at 0x7f32dea15d30>
new方法实际是静态方法,构建对象的方法
单例模式
- 1
class Earth:
def __new__(cls):
if not hasattr(cls,"instance"): #hasattr看一下这个对象里面有没有instance
# 如果有,返回true,如果没有,返回false
cls.instance = super().__new__(cls)
return cls.instance
def __init__(self):
self.name = 'earth'
e = Earth()
print(e,id(e))
a = Earth()
print(a,id(a))
#<__main__.Earth object at 0x7f5cca410cc0> 140036506979520
<__main__.Earth object at 0x7f5cca410cc0> 140036506979520
- 在上面例子中,两个实例的id是相同的,意味着这两个其实引用的是同一个实例,是一个实例的不同名字
- 单例模式:防止内存浪费,一个类只实例一次e和a是一样的,是同一个实例
- 2
class User(object):
__instance = None #私有的类属性
def __init__(self,name):
self.name = name
@classmethod
def get_instance(cls,name):
if not cls.__instance: #如果 __instance 为 NONE,执行下行代码
cls.__instance = User(name)
return cls.__instance
u1 = User.get_instance('xiaoge') #此时cls.__instance不存在,判断后执行cls.__instance = User('xiaoge')
u2 = User.get_instance('liangzai') #此时时cls.__instance已经存在,为User('xiaoge'),此时直接return cls.__instance
u3 = User.get_instance('handsome') #此时时cls.__instance已经存在,为User('xiaoge'),此时直接return cls.__instance
print(u1 == u2) #True
print('u1对象的内存地址:%s,u2对象的内存地址:%s' % (id(u1),id(u2)))
#u1对象的内存地址:140022761147472,u2对象的内存地址:140022761147472
print(u1.name,u2.name) #xiaoge xiaoge
- 3
class User(object):
__instance = None #私有的类属性
def __init__(self,name):
self.name = name
def __new__(cls,name):
if not cls.__instance: #如果 __instance 为 NONE
cls.__instance = object.__new__(cls)
return cls.__instance
u1 = User('xiaoge') #此时cls.__instance为None,判断后执行cls.__instance = User('xiaoge')
u2 = User('liangzai') #此时时cls.__instance已经存在,为User('xiaoge'),此时直接return cls.__instance
print(u1 == u2) #True
print(u1.name,u2.name) #liangzai laingzai #因为先执行__new__,再执行__init__,所以name属性和后实例化的对象的name一致
print('u1对象的内存地址:%s,u2对象的内存地址:%s' % (id(u1),id(u2))) #u1对象的内存地址:140713723821360,u2对象的内存地址:140713723821360
工厂模式
- 工厂模式是最常用的实例化对象模式
- 用工厂方法代替new操作,对染可能多做一些工作,但会给系统带来更大的可拓展性和可维护性
- 原例
class Person(object):
def __init__(self,name):
self.name = name
def work(self):
print(self.name + '开始工作了')
#person完成work,需要使用一把斧头
#在原始社会,人需要石斧
# axe = StoneAxe('花岗岩斧头') #不用石斧,要用钢斧
axe = SteelAxe('钢铁斧头')
axe.cut_tree()
class Axe(object): #斧子的父类
def __init__(self,name):
self.name = name
def cut_tree(self):
print('%s开始砍树' % self.name)
class StoneAxe(Axe): #石斧
def cut_tree(self):
print('用石斧砍树')
class SteelAxe(Axe):
def cut_tree(self):
print('用钢斧砍树')
# p = Person('原始人') #改现代人用钢斧
p = Person('现代人') #人和斧头存在较强的依赖,不利于维护
p.work()
简单工厂
- 两种方法
- 静态的工厂类
- 用全局函数改写工厂类,不是面向对象编程,用的不多
- 解决两个类或者对象存在较强依赖关系,优化
class Person(object):
"""
人需要用斧头砍树,通过第三方(工厂)获得想要的斧头去砍树
"""
def __init__(self,name):
self.name = name
def work(self,axe_type):
print(self.name + '开始工作了')
#person完成work,需要使用一把斧头
axe = Factory.create_axe(axe_type)
axe.cut_tree()
class Axe(object): #斧子的父类
def __init__(self,name):
self.name = name
def cut_tree(self):
print('%s开始砍树' % self.name)
class StoneAxe(Axe): #石斧
def cut_tree(self):
print('用石斧砍树')
class SteelAxe(Axe):
def cut_tree(self):
print('用钢斧砍树')
class Factory(object): #简单工厂类,第三方
@staticmethod #静态工厂类
def create_axe(type): #根据用户指定的类型生产斧头
if type == 'stone':
return StoneAxe('花岗岩斧头')
elif type == 'steel':
return SteelAxe('钢铁斧头')
else:
print('传入斧头类型不对')
p = Person('原始人') #人和斧头存在较强的依赖,不利于维护
p.work('stone')
工厂方法模式
- 工厂方法模式去掉了简单工厂模式中工厂方法的静态方法,使得它可以被子类继承,就是工厂类被具体工厂继承,这样在简单工厂模式里集中在工厂方法上的压力可以由工厂方法模式里不同的工厂子类来分担
- 抽象的工厂类提供了一个创建对象的方法,也叫做工厂方法 (用于多产品)
- 抽象工厂角色(Factory):这是工厂方法模式的核心,他与应用程序无关,是具体工厂角色必须实现的接口或者必须继承的父类
- 具体工厂角色(StoneAxeFactory,SteelAxeFactory):它含有和具体业务逻辑有关的代码,由应用程序调用以创建对应的具体产品的对象
- 抽象产品角色:它是具体产品继承的父类或者是实现的接口,抽象产品一般为父类
- 具体产品角色:具体工厂角色所创建的对象就是此角色的实例,由一个具体类实现
- 例
class Person(object):
"""
人需要用斧头砍树,通过第三方(工厂)获得想要的斧头去砍树
"""
def __init__(self,name):
self.name = name
def work(self):
print(self.name + '开始工作了')
#person完成work,需要使用一把斧头
axe = SteelAxeFactory().create_axe()
axe.cut_tree()
class Axe(object): #斧子的父类
def __init__(self,name):
self.name = name
def cut_tree(self):
print('%s开始砍树' % self.name)
class StoneAxe(Axe): #石斧
def cut_tree(self):
print('用石斧砍树')
class SteelAxe(Axe):
def cut_tree(self):
print('用钢斧砍树')
class Factory(object): #工厂方法
def create_axe(type): #根据用户指定的类型生产斧头
pass
class StoneAxeFactory(Factory):
def create_axe(self):
return StoneAxe('花岗岩斧头')
class SteelAxeFactory(Factory):
def create_axe(self):
return SteelAxe('钢铁斧头')
p = Person('wenwen')
p.work()
定制属性访问
hasattr(object, name)
- 判断一个对象里面是否有name属性或者name方法,返回BOOL值,有name特性返回True, 否则返回False
- 需要注意的是name要用括号括起来
getattr(object, name[,default])
- 获取对象object的属性或者方法,如果存在打印出来,如果不存在,打印出默认值,默认值可选
- 需要注意的是,如果是返回的对象的方法,返回的是方法的内存地址,如果需要运行这个方法,可以在后面添加一对括号
setattr(object, name, values)
- 给对象的属性赋值,若属性不存在,先创建再赋值
delattr删(object,name)
案例:
class Earth:
color = "red"
def __init__(self,length,width):
self.length = length
self.width = width
def func(self):
print("哈哈哈")
def area(self):
areas = self.length * self.width
return areas
r = Earth(10,20)
a = r.area()
print(hasattr(r,"name")) #false
print(hasattr(r,"length")) #true
print(hasattr(r,"area")) #true
print(hasattr(Earth,"area")) #true
print(getattr(r,"color")) #red
print(getattr(r,"length")) #10
getattr(r,"func")() #哈哈哈
print(hasattr(r,"name")) #false
setattr(r,"name","Rectangle") #有则改,无则增
print(hasattr(r,"name")) #true
- 在类里面实例化另一个类,对这个实例做访问时,需要定义
__get__()
,__set__()
,__delete__()
方法 - 描述符本质就是一个新式类,在这个新式类中,至少实现了
__get__()
,__set__()
,__delete__()
中的一个__get__()
:调用一个属性时,触发__set__()
:为一个属性赋值时,触发__delete__()
:采用del删除属性时,触发
- 描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)
- 例
class MyAttribute:
def __get__(self, instance, owner):
print("get")
def __set__(self, instance, value):
print("set")
def __delete__(self, instance):
print("del")
class MyClass:
m = MyAttribute()
def __del__(self): #创建对象就被调用
print("instance delete")
c = MyClass() #instance delete
c.m #get
c.m = 1 # set
del c.m #del 删除后,所占内存被回收
__del__
:对于开发者来说很少会直接销毁对象(如果需要,应该使用del关键字销毁)- 不管是手动调用del还是由python自动回收都会触发
__del__
方法执行 - 例
class MyClass:
def __init__(self):
print('对象初始化')
def __del__(self):
print('haha')
# def __delete__(self):
# print('对象即将被销毁')
#只创建m1对象,当程序结束,python回收所占内存,所以也会调用__del__方法
m1 = MyClass() #对象初始化
haha
------------------------------------------------------------------------------------------
m1 = MyClass() #对象初始化 haha #创建对象,相当于m1和MyClass()建立了连接,m1指向MyClass()的内存地址
print(m1) #<__main__.MyClass object at 0x7f59ee803c88>
m2 = m1 #将m1赋值给m2,相当于m2也指向MyClass()的内存地址
print(m2) #<__main__.MyClass object at 0x7f59ee803c88>
del m1 #删除m1和MyClass()的连接,m2和MyClass()的连接还存在,所以MyClass()仍占据原来的内存地址
print(m2) #<__main__.MyClass object at 0x7f59ee803c88>
print('----------')
del m2 #haha #删除m2,删除了m2和MyClass()的连接,由于没有连接,销毁MyClass()所占的内存,内存地址没了,才调用__del__方法
print('**********')
#对象初始化
<__main__.MyClass object at 0x7fe3640b0cc0>
<__main__.MyClass object at 0x7fe3640b0cc0>
<__main__.MyClass object at 0x7fe3640b0cc0>
----------
haha
**********
装饰器
- 装饰器其实就是一个闭包,把一个函数当做参数然后返回给一个替代版函数
- 装饰器有两个特性
- 一是可以把被装饰的函数替换成其他函数
- 二是可以在加载模块时候立即执行
- 装饰器功能
- 引入日志
- 函数执行时间统计
- 执行函数前预备处理
- 执行函数后清理功能
- 权限校验等场景
- 缓存
- 闭包:
- 内部函数对外部函数作用域里变量的引用,内部函数为闭包
def f1(func):
def f2(y):
print("f2")
return func(y) + 1
return f2
def gun(m):
print("run gunning")
# print(m * m)
return m * m
fun = f1(gun) #把函数gun当做参数传入f1函数,再通过return传入f2函数
print(fun(10)) #f2
run gunning
101
- 装饰器:装饰器的目的是不改变函数里面的代码给函数增加功能
def f1(func):
def f2(y):
print("f2")
return func(y) + 1
return f2
@f1 #装饰器/语法糖 把装饰器下面的函数名当做参数传入f1函数
def gun(m):
print("run gunning")
return m * m
print(gun(10)) #f2
run gunning
101
无参数装饰器
def make_bold(func):
def wrapped():
return '<b>' + func() + '</b>'
return wrapped
def make_italic(func):
def wrapped():
return '<i>' + func() + '</i>'
return wrapped
@make_italic
@make_bold
def test1():
return 'hello world1'
print(test1())
#<i><b>hello world1</b></i>
- 一个函数可以加多个装饰器,使用顺序为从内到外,先使用离自己近的,在使用离自己远的
有参数装饰器
from time import ctime, sleep
def timefunc(func):
#因为 foo 函数有参数,所以这个内置函数也要有和 foo 函数一样的参数,才能被成功使用
#这种装饰器只能用在有两个参数的函数中
def wrappedfunc(a,b):
print('%s called at %s' % (func.__name__, ctime()))
print(a,b)
func(a,b)
return wrappedfunc
@timefunc
def foo(a,b):
print(a+b)
foo(3,5)
sleep(2)
foo(2,4)
#foo called at Wed May 22 16:41:23 2019
3 5
8
foo called at Wed May 22 16:41:25 2019
2 4
6
不定长参数装饰器
- 通用装饰器
- 参数写成不定长
- 要有返回值,返回的是要用装饰器的函数的调用
from time import ctime, sleep
def timefunc(func):
def wrappedfunc(*args, **kwargs): #因为foo函数有参数,所以这个内置函数也要有和foo函数一样的参数,才能被成功使用
print('%s called at %s' % (func.__name__, ctime()))
print(*args, **kwargs)
func(*args, **kwargs)
return wrappedfunc
@timefunc
def foo(a,b):
print(a+b)
foo(3,5)
sleep(2)
foo(2,4)
#foo called at Wed May 22 16:41:23 2019
3 5
8
foo called at Wed May 22 16:41:25 2019
2 4
6
巩固基本功
- 偶然发现个问题,多次尝试,对return有了新的认识
内置装饰器
class Rectangle:
def __init__(self,length,width):
self.length = length
self.width = width
def area(self):
areas = self.length * self.width
return areas
@property #就像访问属性一样
def area(self):
return self.width * self.length
@staticmethod #静态方法
def func(): #self在调用的时候会报错
print("staticmenthod func")
@classmethod #类方法
def show(cls):
print(cls)
print("show fun")
动态语言
- 动态语言:在运行过程中可以修改代码,如PHP,Python,JavaScript,ruby等
- 静态语言:编译时已经确定好代码,运行过程中不能修改,如c,c++等
- 在运行中可以为对象添加属性和方法
例
- ipython3中
In [1]: class Person(object):
...: def __init__(self,name,age):
...: self.name = name
...: self.age = age
...:
#添加类属性
In [2]: Person.addr = '肇东'
In [3]: p = Person('xiaoge',15)
In [4]: p.addr
Out[4]: '肇东'
In [5]: p2 = Person('jingyi',14)
In [6]: p2.addr
Out[6]: '肇东'
#添加实例属性
In [27]: p.sex = 'man'
In [28]: dir(p)
Out[28]:
['__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__',
'addr',
'age',
'name',
'sex',
'show_info']
In [7]: def show_info(self):
...: print(self.name)
...: print(self.age)
...:
In [9]: p.show_info = show_info
In [10]: show_info
Out[10]: <function __main__.show_info(self)>
In [13]: p.show_info()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-13-7a2c006f6f60> in <module>
----> 1 p.show_info()
TypeError: show_info() missing 1 required positional argument: 'self'
#添加实例方法
In [14]: import types #出现上述问题导入types
# types.MethodType传入两个参数,第一个参数show_info是要加的方法名,第二个参数p是实例化的对象名 ,这个过程实际就是把p做为self,往里面传参
In [15]: p.show_info = types.MethodType(show_info,p)
In [16]: p.show_info
Out[16]: <bound method show_info of <__main__.Person object at 0x7f63f8e0f5c0>>
In [17]: p.show_info()
xiaoge
15
In [25]: a = types.MethodType(show_info,p2)
In [26]: a()
jingyi
14
#添加类方法
In [29]: @classmethod
...: def fun1(cls):
...: print('classMethod')
...:
In [30]: Person.fun1 = fun1
In [31]: p.fun1()
classMethod
In [32]: p2.fun1()
classMethod
#添加静态方法
In [33]: @staticmethod
...: def fun2(a,b):
...: return a + b
...:
In [34]: Person.fun2 = fun2
In [35]: print(p.fun2(2,3))
5
In [36]: print(p2.fun2(1,2))
3
#限制修改对象的属性,设定类时不想让对象随便添加属性,就在定义类的下面__slots__ = ('限制对象可以添加的属性'),其他的属性就无法添加,防止别人乱添加
In [37]: class Student(object):
...: __slots__ = ('name','age')
...:
In [38]: stu = Student()
In [39]: stu.name = 'haha'
In [40]: stu.name
Out[40]: 'haha'
In [41]: stu.age = 20
In [42]: stu.age
Out[42]: 20
In [43]: stu.sex = 'boy'
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-43-df04242a9dfd> in <module>
----> 1 stu.sex = 'boy'
AttributeError: 'Student' object has no attribute 'sex'
类装饰器
- 装饰器函数其实是一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象
- 一般callable对象都是函数,但也有例外,只要某个对象重写了
__call__()
方法,那么这个对象就是callable的 - 类当函数用
class Test(object):
def __call__(self):
print('call')
t = Test()
t() #call
- 类当装饰器
class Test_Class:
def __init__(self,func): #func_test传入func作为参数
self.func = func
def __call__(self):
print("类")
return self.func
@Test_Class #用类名做语法糖/装饰器,生成一个Test的对象,会调用__init__方法,把装饰的函数作为参数传入
def func_test():
print("这是个测试函数")
func_test() #调用Test一个对象的__call__方法
print(func_test())
#类
类
<function func_test at 0x7f7c72a07e18>