文章目录
-
-
- 建议不要光看,要多动手敲代码。眼过千遭,不如手读一遍。
- 面向对象
-
- 魔法方法
-
- `__new__`方法
- `__init__`方法
- `__str__`方法
- `__del__`方法
- 其他方法
- 引用计数
- 继承
- 重写
- 类属性
- 实例属性
- 有关类方法、实例方法、静态方法的笔记参考了`蔷薇Nina `这位朋友的笔记
-
- 类方法
- 实例方法
- 实例方法中self的含义
- 静态方法
- 动态添加属性以及方法
- 解耦合
- 私有化
- property的使用
- `__slots__`的作用
- 元类
- `__metaclass__`属性
- 内建属性
- `__getattribute__`的坑
- `__getattr__`方法
- `__setattr__`方法
- 描述符
-
- 描述符定义
- 描述符种类及优先级
-
- 类属性>数据描述符
- 数据描述符>实例属性
- 实例属性>非数据描述符
- 描述符使用注意点
- 描述符使用
-
笔记中代码均可运行在Jupyter NoteBook下(实际上Jupyter-lab使用体验也很棒)。
建议不要光看,要多动手敲代码。眼过千遭,不如手读一遍。
相关笔记的jupiter运行代码已经上传,请在资源中自行下载。
面向对象
'''
类:
class ClassName:
pass
对代码基本相同的类抽取基类:当不同类中的方法基本相同时,
可以抽取出一个基类,用来简化代码
属性:
实例属性可以通过在__init__方法中设置,不必刻意写出
方法:
def func(self):
pass
# 这里的self相当于java中的this,只是python中写成了self
'''
魔法方法
__new__
方法
__new__
通常
__new__
都不需要定义,在元类编程中才需要,它可以控制类的生成过程。
'''
__new__至少要有一个参数cls,代表要实例化的类,
此参数在实例化时由python解释器自动提供
__new__必须要有返回值,返回实例化出来的实例,
这点在自己实现__new__时要特别注意,
可以return父类__new__出来的实例,或者直接是object的__new__出来的实例
__new__(cls):这个方法在类被引用时会被执行
(自动的,如果不被创建,类会调用默认的__new__方法)
这里的cls此时指向的是类的名字[其实类是通过父类object.__new__(cls)来生成的]
__new__方法只负责创建,__init__方法只负责初始化
__init__的self参数就是__new__返回的实例,__init__在__new__的基础上完成初始化动作,__init__不需要返回值
注意,在创建对象时最先调用__new__方法,这时向类中添加参数会报错,
所以这时需要在__new__方法中设置虚参,只是一个样子,不需要用到
'''
# 在此比较java和c++中的构造方法,构造方法其实包含了Python中的这两个方法
'''单例模式:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,单例模式是一种对象创建型模式。'''
# 创建单例对象:不管创建多少只有一个对象
class A(object):
pass
d = A()
e = A()
print(id(d))
print(id(e))
#注意,这里的两个print的输出值并不相同
#这里不是单例模式
"""单例模式可以通过类变量来实现"""
# 这个单例模式只是一个例子,可以看出只能创建单例模式中的类属性,方法的创建就要添加参数
class A(object):
__num = None
def __new__(cls):
if cls. __num == None: # 这里判断是否为第一次实例化
cls.__num = object.__new__(cls) # 第一次实例化,进行实例化
return cls.__num
else :
# 不是第一次实例化:返回上面生成的cls.__num
return cls.__num
# 这里依据类属性在实例化对象中公用的原理,创建单例模式
'''在实例化a时,将A类的私有属性__num'''
a = A()
b = A()
print(id(a))
print(id(b))
#此时打印的a、b的id是相同的
#也就是表明此时是单例模式
139934035060440
139934035059712
139934035060216
139934035060216
__init__
方法
__init__
用于初始化:初始化函数,将变量绑定到实例中,更新实例的
__dict__
字典。
其中第一个参数self就是
__new__
的返回值,是类的实例。
__new__
方法先于
__init__
方法执行。
# __init__方法:
class Demo:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return ('your name is {name},your age is {age}'.format(name = self.name,
age = self.age))
demo = Demo('shj', 22)
print(demo)
your name is shj,your age is 22
__str__
方法
__str__
用于获取描述信息
# __str__方法:
class Demo:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return ('your name is {name},your age is {age}'.format(name = self.name,
age = self.age))
demo = Demo('shj', 22)
print(demo)
your name is shj,your age is 22
__del__
方法
__del__
析构函数,释放对象时使用
在硬链接的obj完全删除时自动调用此方法,可视为善后处理
其他方法
''' 这些方法都是类(Class)专有方法
__repr__ : 打印,转换
__setitem__ : 按照索引赋值
__getitem__: 按照索引获取值
__len__: 获得长度
__cmp__: 比较运算
__call__: 函数调用
__add__: 加运算
__sub__: 减运算
__mul__: 乘运算
__truediv__: 除运算
__mod__: 求余运算
__pow__: 乘方
'''
引用计数
'''
sys模块中的getrefcount()方法
sys.getrefcount() :用于查看一个对象被引用的次数
返回的值是引用次数+1(次数=返回值-1)
'''
# 引用计数例子
from sys import getrefcount
class DD:
def __init__(self,info):
self.info = info
def __str__(self):
return 'this is {} Demo'.format(self.info)
d1 = DD('d1')
d2 = DD('d2')
print(d1)
print(d2)
print('*'*8)
count = getrefcount(DD)-1
print('the getrefcount is {}'.format(count))
this is d1 Demo
this is d2 Demo
********
the getrefcount is 6
继承
'''
class sonClass(FatherClass):
pass
世袭:子类可以继承父类(基类),也可以继承父类的父类
多继承
class sonClass(Base1, Base2, Base3):
pass
继承后对方法的查找顺序:从左至右
可用className.__mro__打印这个类可调用的类
私有方法、私有属性不能被继承
但是调用的公有方法中包含私有方法或属性,这个公有方法可以被继承
'''
# 多继承例子
#类定义
class people:
#定义基本属性
name = ''
age = 0
#定义私有属性,私有属性在类外部无法直接进行访问
__weight = 0
#定义构造方法
def __init__(self,n,a,w):
self.name = n
self.age = a
self.__weight = w
def speak(self):
print("%s 说: 我 %d 岁。" %(self.name,self.age))
#单继承示例
class student(people):
grade = ''
def __init__(self,n,a,w,g):
#调用父类的构函
people.__init__(self,n,a,w)
self.grade = g
#覆写父类的方法
def speak(self):
print("%s 说: 我 %d 岁了,我在读 %d 年级"%(self.name,self.age,self.grade))
#另一个类,多重继承之前的准备
class speaker():
topic = ''
name = ''
def __init__(self,n,t):
self.name = n
self.topic = t
def speak(self):
print("我叫 %s,我是一个演说家,我演讲的主题是 %s"%(self.name,self.topic))
#多重继承
class sample(speaker,student):
a =''
def __init__(self,n,a,w,g,t):
student.__init__(self,n,a,w,g)
speaker.__init__(self,n,t)
test = sample("Tim",25,80,4,"Python")
test.speak() #方法名同,默认调用的是在括号中排前地父类的方法
print('\nthe class sample can use BASE CLASS IS:\n {}'.format(sample.__mro__))
我叫 Tim,我是一个演说家,我演讲的主题是 Python
the class sample can use BASE CLASS IS:
(<class '__main__.sample'>, <class '__main__.speaker'>, <class '__main__.student'>, <class '__main__.people'>, <class 'object'>)
重写
'''
子类重写父类方法和java相同
调用被重写的方法:
BaseClass.funcName()
super.funcName()
'''
'''多态:定义的方法根据传递进的方法的不同而得到不同的结果'''
# 一个多态的小例子
class F1(object):
def show(self):
print ('F1.show')
class S1(F1):
def show(self):
print ('S1.show')
class S2(F1):
def show(self):
print ('S2.show')
# 由于在Java或C#中定义函数参数时,必须指定参数的类型
# 为了让Func函数既可以执行S1对象的show方法,又可以执行S2对象的show方法,所以,定义了一个S1和S2类的父类
# 而实际传入的参数是:S1对象和S2对象
'''
对java或C#
def Func(F1 obj):
"""Func函数需要接收一个F1类型或者F1子类的类型"""
print (obj.show())
'''
# python
def func(obj):
obj.show()
print("*"*8)
s1_obj = S1()
func(s1_obj) # 在Func函数中传入S1类的对象 s1_obj,执行 S1 的show方法,结果:S1.show
s2_obj = S2()
func(s2_obj) # 在Func函数中传入Ss类的对象 ss_obj,执行 Ss 的show方法,结果:S2.show
S1.show
********
S2.show
********
类属性
'''
定义在class内部,但是在方法外部的变量称为类的属性
类属性属于类,并且类属性在实例间共享
'''
实例属性
'''
和具体的某个实例对象有关,在类内部的实例属性不同实例对象共用,类外部单独创建的实例属性不共用
实例属性和类属性重名会强制覆盖类属性,但是无法通过实例属性对类属性进行更改
'''
有关类方法、实例方法、静态方法的笔记参考了 蔷薇Nina
这位朋友的笔记
蔷薇Nina
导向链接
https://www.cnblogs.com/wcwnina/p/8644892.html
类方法
类的方法,需要在方法前添加
@classmethod
注意:类方法必须有有一个
cls
变量做形参,这个
cls
表示类本身(当前类对象)
,通过它来传递类的属性和方法(不能传实例的属性和方法);
调用:实例对象和类对象都可以调用。
@classmethod
def classFunc(cls): # 这里必须要定义一个形参,名称无所谓,惯用cls,这个变量表示类本身
pass
原则上,类方法是将类本身作为对象进行操作的方法。
假设有个方法,且这个方法在逻辑上采用类本身作为对象来调用更合理,那么这个方法就可以定义为类方法。
另外,如果需要继承,也可以定义为类方法。
# 假设我有一个学生类和一个班级类,想要实现的功能为:
# 执行班级人数增加的操作、获得班级的总人数;
# 学生类继承自班级类,每实例化一个学生,班级人数都能增加;
# 最后,我想定义一些学生,获得班级中的总人数。
# 思考:这个问题用类方法做比较合适,为什么?因为我实例化的是学生,
# 但是如果我从学生这一个实例中获得班级总人数,在逻辑上显然是不合理的。
# 同时,如果想要获得班级总人数,如果生成一个班级的实例也是没有必要的。
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import hashlib
from math import pi
class ClassRoom(object):
__num = 0
@classmethod
def add_num(cls):
cls.__num += 1
@classmethod
def get_num(cls):
return cls.__num
def __new__(self):
ClassRoom.add_num()
return super().__new__(self)
class Student(ClassRoom):
def __init__(self):
self.name = ""
stu_a = Student()
stu_a.name = "lee"
stu_b = Student()
stu_b.name = "sh"
print("现在的学生人数为:", ClassRoom.get_num())
实例方法
实例方法:第一个参数必须是实例对象,该参数名一般约定为“self”,
通过它来传递实例的属性和方法(也可以传类的属性和方法);
调用:只能由实例对象调用。
类中定义的方法没有特殊标注都是实例方法
self 必须写
def intanceFunc(self): # self 表示实例本身
pass
实例方法中self的含义
'''
self代表的是实例,不是类
'''
# 例子
class Demo(object):
def get(self):
print(self.__class__) # 打印self的类
print(self) # 打印self
demo = Demo()
demo.get()
print(demo.get)
<class '__main__.Demo'>
<__main__.Demo object at 0x7f17e820d390>
<bound method Demo.get of <__main__.Demo object at 0x7f17e820d390>>
'''
实例方法中的self必须写,不写的话将变成类方法,
并且此类方法只能用类名调用,用实例名调用会报错,
因为少self参数,无法实例化
'''
# 例子
class Demo(object):
# 定义和调用都有self
def get(self):
print(self) # 打印self
print(self.__class__) # 打印self的类
demo = Demo()
print('实例方法中有self:')
demo.get()
print("*"*8)
class Test(object):
# 定义时无self,调用时有self
def get():
print(self)
# 异常处理
try:
test = Test()
test.get()
except Exception as result:
print('实例化方法中无self会抛出异常:')
print(result)
class Test_1(object):
# 定义和调用均无self
def get():
print('方法定义时无self,调用时也没有self')
print(__class__)
Test_1.get()
实例方法中有self:
<__main__.Demo object at 0x7f44eda10208>
<class '__main__.Demo'>
********
实例化方法中无self会抛出异常:
get() takes 0 positional arguments but 1 was given
方法定义时无self,调用时也没有self
<class '__main__.Test_1'>
'''
继承时,实例为被实例化的类的实例,不是定义了实例所调用的方法的类的实例
'''
# 例子
class People(object):
def infos(self):
str = 'i am People{}'.format(self)
return str
class Man(People):
def about(self):
str = 'I am man {}'.format(self)
return str
man = Man()
print('Man的实例man调用Man的about()实例方法:\n\t', man.about())
print('Man的实例man调用Man的父类People的infos()实例方法:\n\t', man.infos())
# 比较上述两个输出结果可以看出,实例是被实例化类Man的实例,而不是定义了实例方法的People类的实例
print('\n*'*2)
people = People()
print('People的实例people调用People类的实例方法infos():\n\t', people.infos())
Man的实例man调用Man的about()实例方法:
I am man <__main__.Man object at 0x7f17e8217f98>
Man的实例man调用Man的父类People的infos()实例方法:
i am People<__main__.Man object at 0x7f17e8217f98>
*
*
People的实例people调用People类的实例方法infos():
i am People<__main__.People object at 0x7f17e8217a90>
'''
在描述符类中,self指的是描述符类的实例
'''
# 例子
class Desc:
def __get__(self, ins, cls):
print('self in Desc: %s ' % self )
print(self, ins, cls)
class Test:
x = Desc()
def prt(self):
print('self in Test: %s' % self)
t = Test()
t.prt()
'''
这里调用的是t.x,也就是说是Test类的实例t的属性x,
由于实例t中并没有定义属性x,所以找到了类属性x,
而该属性是描述符属性,为Desc类的实例而有,
所以此处并没有顶用Test的任何方法。
那么我们如果直接通过类来调用属性x也可以得到相同的结果。
[把t.x改为Test.x]
'''
t.x
self in Test: <__main__.Test object at 0x7f44eda29748>
self in Desc: <__main__.Desc object at 0x7f44eda29438>
<__main__.Desc object at 0x7f44eda29438> <__main__.Test object at 0x7f44eda29748> <class '__main__.Test'>
静态方法
静态方法:在类中,方法前添加@staticmethod
静态方法可以不定义参数,也可以定义参数(cls、self在静态方法中没啥意义)
调用:实例对象和类对象都可以调用。
@staticmethod
def staticFunc([self]): # 静态方法的self可写可不写,静态方法相当于一个普通方法
pass
逻辑上属于类,但是和类本身没有关系,也就是说在静态方法中,不会涉及到类中的属性和方法的操作。
#! /usr/bin/env python3
# -*- coding:utf-8 -*-
"""this is a test py flie"""
# 类属性 实例属性 类方法 实例方法 静态方法 例子
class classDemo:
# 类属性
msg = 'class msg'
# 初始化
def __init__(self, info): # 这里info是实例属性
self.info = info
# 实例方法
def about(self):
return self.info
# 类方法
@classmethod
def classFunc(cls):
msg = 'class method'
return msg
# 静态方法
@staticmethod
def staticFunc():
return 'static method'
# 声明两个实例对象 classDemo中的参数传给self.info
demo1 = classDemo('demo1')
demo2 = classDemo('demo2')
# 在实例对象外部为不同实例对象创建不同的实例属性,类外部创建的实例属性不共用
demo1.flag = 'ddd'
demo2.key = 'asdemo'
print(demo1.flag) # ddd
# 这里demo2调用了非共用实例属性,会报错
try:
print(demo2.flag)
except Exception as result:
print(result) # 'classDemo' object has no attribute 'flag'
print("*" * 8)
# info是二者共有的实例属性,不会报错
print(demo1.info) # demo1
print(demo2.info) # demo2
print("*" * 8)
# 类属性可通过实例对象调用
print('use by instance:', demo1.msg) # use by instance: class msg
# 类属性还可通过类名调用
print('use by class name: ', classDemo.msg) # use by class name: class msg
print("*" * 8)
# 类属性不能通过同名实例属性更改
demo1.msg = 'demo1 msg'
print('demo1.msg={0},class.msg={1}'.format(demo1.msg, classDemo.msg))
# demo1.msg=demo1 msg,class.msg=class msg
classDemo.msg = 'changed class msg'
print('demo1.msg={0},class.msg={1}'.format(demo1.msg, classDemo.msg))
# demo1.msg=demo1 msg,class.msg=changed class msg
print("*" * 8)
# 类方法同样可以通过类名和实例对象调用
print('use by instance:', demo1.classFunc()) # use by instance: class method
print('use by class name: ', classDemo.classFunc()) # use by class name: class method
print("*" * 8)
# 静态方法同样可通过类名和实例对象调用
print('use by instance:', demo1.staticFunc()) # use by instance: static method
print('use by class name: ', classDemo.staticFunc()) # use by class name: static method
ddd
'classDemo' object has no attribute 'flag'
********
demo1
demo2
********
use by instance: class msg
use by class name: class msg
********
demo1.msg=demo1 msg,class.msg=class msg
demo1.msg=demo1 msg,class.msg=changed class msg
********
use by instance: class method
use by class name: class method
********
use by instance: static method
use by class name: static method
动态添加属性以及方法
'''
添加属性就是简单的"."操作,但是添加方法时,需要用到模块,types
instanceName.method = types.MethodType(methodName, instance)
'''
# 动态添加方法例子
class Demo(object):
def __init__(self, name):
self.name = name
# Demo类中并没有tmp方法,现在为其添加tmp方法
# 先定义方法
def tmp(self):
print("this is a test function for {}".format(self.name))
d = Demo("666")
try:
d.tmp()
except Exception as result:
print('目前未对Demo添加tmp方法,使用tmp方法会报错')
print(result) # 'Demo' object has no attribute 'tmp'
# 导入types模块
import types
# 用types.MethodType(func, instanceName) 添加了tmp方法
d.tmp = types.MethodType(tmp, d)
print('\nafter add a method') # after add a method
d.tmp() # this is a test function for 666
#这样就添加了一个方法
目前未对Demo添加tmp方法,使用tmp方法会报错
'Demo' object has no attribute 'tmp'
after add a method
this is a test function for 666
'''
types.MethodType的作用:将对象和要添加的方法绑定
这里需要特别注意:用到types.MethodType时,
只是用来添加实例对象的方法,对静态方法(staticmethod)
和类方法(classmethod)只是需要用"."操作:
className.p = p这种方式就可以直接添加
'''
# 对类添加类方法和静态方法 例子
class Demo(object):
def __init__(self, name):
self.name = name
@staticmethod
def addstaticFunc():
print("static method")
@classmethod
def addClassFunc(cls):
print("class method")
demo = Demo(666)
#添加静态方法
Demo.addstaticFunc = addstaticFunc
#添加类方法
Demo.addClassFunc = addClassFunc
#使用静态方法和类方法
Demo.addstaticFunc() # static method
print('*'*8)
demo.addClassFunc() # class method
static method
********
class method
解耦合
'''
对于两个类有交互的时候,可以通过中间件(函数或其他方式)来解决耦合
但是在开发中,尽量使用一个类型,即用类尽量都用类,用方法尽量都用方法
'''
# 工厂模式 解耦合 例子
class Factory(object):
def factFun(self):
return fac
class A(object):
def __int__(self):
self.factory = Factory()
def useB(self):
return use_b
class B(object):
def theBFun(self):
return theB
私有化
'''
name:公有
_name (单_):私有,from XX improt * 禁止导入,类和子类可以访问
__name (双_):私有,避免和子类发生冲突,在外部无法访问
__name__(双_)私有,特有的魔法方法或属性,个人最好不要创建
name_:用于区别与关键字重复
只要开头有_,基本from xx import * 就无法导入,但是只是导入模块,还是可以用的
name mangLing(名字重整)为了防止对私有属性或方法的使用(访问时可以_ClassName.__name)
'''
property的使用
'''
这里需要建立像Java中的setter和getter方法
property通过setter方法和getter方法,
将本不可在外部通过 对象.属性 的调用方式成功实现了
'''
# 例子
class Test(object):
def __init__(self):
self.__num = 0
def getNum(self):
return self.__num
def setNum(self,newNum):
self.__num = newNum
new = property(getNum, setNum)
t = Test()
print('实例未对私有属性赋值:',t.new)
t.new = 666 # 实例对私有属性赋值
print('实例对私有属性赋值后:',t.new)
print('用getter方法获取私有属性的值:',t.getNum())
实例未对私有属性赋值: 0
实例对私有属性赋值后: 666
用getter方法获取私有属性的值: 666
'''
property也可以用装饰器的方法调用
'''
# 例子
class Test(object):
def __init__(self):
self.__num = 0
@property
def num(self):
"""getter"""
return self.__num
@num.setter
def num(self, newNum):
"""setter"""
self.__num = newNum
# 这里就不用写property(getter,setter)方法了
t = Test()
t.num
print('实例未对私有属性赋值:',t.num)
t.num = 666 # 实例对私有属性赋值
print('实例对私有属性赋值后:',t.num)
实例未对私有属性赋值: 0
实例对私有属性赋值后: 666
__slots__
的作用
__slots__
通过在类中用
__slots__
来限制该类实例能添加的属性(__slots__中有的属性才可添加)
__slots__
只对当前类的属性有限制作用,对子类并没有作用
极其不建议用此操作
class Demo(object):
__slots__ = ('name', 'age') # 类Demo只能添加name,age两个属性,添加其他属性会报错
# __slots__限制实例属性的例子
class Person(object):
__slots__ = ('name', 'age')
p = Person()
p.name = 'jack Ma'
p.age = 50
# 这里添加了不允许的实例属性
p.qq = 123456
---------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-98-136e7cfed380> in <module>
13
14 # 这里添加了不允许的实例属性
---> 15 p.qq = 123456
AttributeError: 'Person' object has no attribute 'qq'
元类
'''
类也是对象,所以也可以在运行时动态的创建
'''
# 动态创建类 例子 这是一个半动态的例子
def choose_classs(name):
if name == 'foo':
class Foo(object):
pass
return Foo
else:
class Lol:
pass
return Lol
myClass = choose_classs('foo')
print(myClass) # 函数返回类
print(myClass()) # 可以通过这个类创建实例
<class '__main__.choose_classs.<locals>.Foo'>
<__main__.choose_classs.<locals>.Foo object at 0x7f44ed9db6d8>
'''
还可以用type来创建类
type除了可以用来查看一个变量的属性,同样也可以用来创建类
type("classname", 由父类名组成的tuple(), 由属性组成的dict{})#注意:这里这种方法只是用来保证版本的兼容性,不建议用
注意:这里的type就相当于一个元类
'''
# type动态创建类
Demo = type('Demo', (object,), {'dd':'dd'})
print(Demo())
print(Demo.dd)
<__main__.Demo object at 0x7f44ee2ce710>
dd
__metaclass__
属性
__metaclass__
用于指定创建class的类
'''
__metaclass__ = FatherName(这里也可以是一坨代码)
#这里是用于定义类的生成方式这里系统默认会指定,
但是自己写过后会自动调用自己的
'''
# 自定义类的生成方式
def upper_attr(future_class_name, future_class_parents, future_class_attr):
#遍历属性字典,把不是__开头的属性名字变为大写
newAttr = {}
for name,value in future_class_attr.items():
if not name.startswith("__"):
newAttr[name.upper()] = value
#调用type来创建一个类
return type(future_class_name, future_class_parents, newAttr)
# python3 用法
class Foo(object, metaclass=upper_attr):
bar = 'bip'
# 在Python2中用如下操作:
# class NewClass(object):
# __metaclass__ = DiyClass
# pass
print(hasattr(Foo, 'bar'))
print(hasattr(Foo, 'BAR'))
f = Foo()
print(f.BAR)
False
True
bip
'''自定义类生成方式完整版'''
# 完整版
class UpperAttrMetaClass(type):
# __new__ 是在__init__之前被调用的特殊方法
# __new__是用来创建对象并返回之的方法
# 而__init__只是用来将传入的参数初始化给对象
# 你很少用到__new__,除非你希望能够控制对象的创建
# 这里,创建的对象是类,我们希望能够自定义它,所以我们这里改写__new__
# 如果你希望的话,你也可以在__init__中做些事情
# 还有一些高级的用法会涉及到改写__call__特殊方法,但是我们这里不用
def __new__(cls, future_class_name, future_class_parents, future_class_attr):
#遍历属性字典,把不是__开头的属性名字变为大写
newAttr = {}
for name,value in future_class_attr.items():
if not name.startswith("__"):
newAttr[name.upper()] = value
# 方法1:通过'type'来做类对象的创建
# return type(future_class_name, future_class_parents, newAttr)
# 方法2:复用type.__new__方法
# 这就是基本的OOP编程,没什么魔法
# return type.__new__(cls, future_class_name, future_class_parents, newAttr)
# 方法3:使用super方法
return super(UpperAttrMetaClass, cls).__new__(cls, future_class_name, future_class_parents, newAttr)
#python2的用法
# class Foo(object):
# __metaclass__ = UpperAttrMetaClass
# bar = 'bip'
# python3的用法
class Foo(object, metaclass = UpperAttrMetaClass):
bar = 'bip'
print(hasattr(Foo, 'bar'))
# 输出: False
print(hasattr(Foo, 'BAR'))
# 输出:True
f = Foo()
print(f.BAR)
# 输出:'bip'
False
True
bip
内建属性
__init__
:创建实例后,赋值时使用,在
__new__
后
__new__
:新建实例
__class__
:实例.
__class__
__str__
:print()如果没有,使用repr结果,美化版repr
__repr__
:类实例 回车 或者print(类实例),打印的结果是程序的表达方式
__del__
:删除实例
__dict__
:vars(类或实例.
__dict__
)
__doc__
:help(类或实例)
__bases__
:类名.
__bases__
__getattribute__
:访问实例属性时,属性拦截器(类属性和实例属性),会在所有属性使用之前调用
可以看做是看门的,访问属性前先走一遍
__getattribute__
才可以
def __getattribute__(self, obj):
pass
# __bases__例子
class C1(object):
pass
class C2(object):
pass
class C3(object):
pass
class C4(C3):
pass
class C5(C1, C2, C4):
pass
print(C5.__bases__)
(<class '__main__.C1'>, <class '__main__.C2'>, <class '__main__.C4'>)
'''
由于__getattribute__拦截对所有属性的访问(包括对__dict__的访问),在使用中要十分小心地避开无限循环的陷阱。
在__getattribute__方法中访问当前实例的属性时,唯一安全的方式是使用基类(超类) 的__getattribute__方法(使用super)。
'''
# __getattribute__ 例子1
class Demo(object):
msg = "this is Demo class"
def __init__(self, name, age):
self.name = name
self.age = age
def __getattribute__(self, attr):
print("the attr: {} is block".format(attr))
try:
return super().__getattribute__(attr)
except AttributeError as result:
print("the attr of input: {} is not exit!".format(attr))
print("the Error is: \n\t", result)
demo = Demo('sh', 24)
print("类调用类属性结果:", Demo.msg)
print("实例调用类属性结果:")
print(demo.msg)
print("实例调用存在的实例属性结果:")
print(demo.name)
print("实例调用不存在的实例属性结果:")
print(demo.what)
# 注意:当访问的属性不存在并重载(覆盖基类对某方法的默认实现)了__getattribute__方法时,
# 该方法不会主动抛出AttributeError异常。
# 例子中捕获的AttributeError异常,是由基类的__getattribute__方法实现并抛出。
类调用类属性结果: this is Demo class
实例调用类属性结果:
the attr: msg is block
this is Demo class
实例调用存在的实例属性结果:
the attr: name is block
sh
实例调用不存在的实例属性结果:
the attr: what is block
the attr of input: what is not exit!
the Error is:
'Demo' object has no attribute 'what'
None
# __getattribute__ 例子2
class Demo(object):
def __init__(self, tmp1):
self.tmp1 = tmp1
self.tmp2 = "shj"
def __getattribute__(self, obj):
print("this obj is {}".format(obj).center(20, "*"))
if obj == "":
print("the input is {}".format(self.obj))
else:
tmp = object.__getattribute__(self, obj)
print("this is not if %s" %tmp)
return tmp
def test(self):
print("this is the function named test")
s = Demo("666")
print('test START')
print(s.tmp1)
print('000'*8)
print(s.tmp2)
s.test()
# 注意这里:这里调用的test方法是显示的一个绑定
# this is not if <bound method Demo.test of <__main__.Demo object at 0x7f2f524ab9e8>>
#这里说明,类中的方法同样作为一个属性
# this is the function named test
#通过这两个注意可以看到,类中的方法其实是不存在的,
# 而是通过创建一个属性,通过对属性绑定方法来达到创建方法的操作
__getattribute__
的坑
__getattribute__
class Demo(object):
def __getattribute__(self, obj):
print("这是一个关于attribute属性的坑")
if obj.startswith("a"):
return "666"
else:
return self.test
def test(self):
print("this is the function test")
d = Demo()
print(d.a)
# print(d.b)
#会让程序死掉
#原因是:当d.b执行时,会调用PDemo类中定义的__getattribute__方法,但是在这个方法的执行过程中
#if条件不满足,所以 程序执行else里面的代码,即return self.test 问题就在这,因为return 需要把
#self.test的值返回,那么首先要获取self.test的值,因为self此时就是d这个对象,所以self.test就是
#d.test 此时要获取d这个对象的test属性,那么就会跳转到__getattribute__方法去执行,即此时产
#生了递归调用,由于这个递归过程中 没有判断什么时候退出,所以这个程序会永无休止的运行下去,又因为
#每次调用函数,就需要保存一些数据,那么随着调用的次数越来越多,最终内存吃光,所以程序 崩溃
#
# 注意:以后不要在__getattribute__方法中调用self.xxxx
这是一个关于attribute属性的坑
666
__getattr__
方法
__getattr__
执行需要满足两个必须条件:一是访问对象属性;二是触发
AttributeError
异常。
# __getattr__方法执行例子
class Demo(object):
msg = "this is Demo class"
def __init__(self, name, age):
self.name = name
self.age = age
def __getattribute__(self, attr):
print("the attr: {} is block".format(attr))
if attr not in Demo.__dict__:
raise AttributeError
def __getattr__(self, attr):
print("the method __getattr_ is runing")
demo = Demo('sh', 24)
demo.what
the attr: what is block
the method __getattr_ is runing
# 重载了__getattribute__方法,却没有主动抛出AttributeError异常的机制,
# 或者抛出一个其它类型的异常,__getattr__方法都不会执行。
class Demo(object):
msg = "this is Demo class"
def __init__(self, name, age):
self.name = name
self.age = age
def __getattribute__(self, attr):
print("the attr: {} is block".format(attr))
# if attr not in Demo.__dict__:
# raise AttributeError
def __getattr__(self, attr):
print("the method __getattr_ is runing")
demo = Demo('sh', 24)
demo.what
the attr: what is block
__setattr__
方法
__setattr__
试图给属性赋值时自动调用该方法
当
__setattr__(self, name, value)
方法和
__getattribute__(self, attr)
方法同在时,每次赋值都会调用这两个方法,
因为
__setattr__(self, name, value)
方法出现赋值操作时必调用,包括在
__init__(self)
方法中的赋值操作
__getattribute__(self, attr)
方法在出现访问属性操作时必调用
# __setattr_例子
class Demo(object):
msg = "this is Demo class"
def __init__(self, name, age):
self.name = name
self.age = age
def __getattribute__(self, attr):
print("the attr: {} is block".format(attr))
return super().__getattribute__(attr)
# if attr not in Demo.__dict__:
# raise AttributeError
def __getattr__(self, attr):
print("the method __getattr_ is runing")
def __setattr__(self, name, value):
print("the method __setattr__ is running")
self.__dict__[name] = value
demo = Demo('sh', 24)
demo.name = "haha"
# 这里出现多次重复输出是因为在__init__()方法初始化时赋值两次,调用__setattr__()两次
# 在自己赋值时:赋值一次,调用__setattr__()方法一次
# 而__getattribute__()方法和__setattr__()方法同在,访问属性和赋值均会依次调用
# 所以,会出现重复输出的结果
the method __setattr__ is running
the attr: __dict__ is block
the method __setattr__ is running
the attr: __dict__ is block
the method __setattr__ is running
the attr: __dict__ is block
# 在__setattr__方法中,不能使用self.attr = value的方法直接给属性赋值(会导致无限循环),
# 而通常的做法是使用self.__dict__[attr] = value的赋值方法
class Demo(object):
msg = "this is Demo class"
def __init__(self, name, age):
self.name = name
self.age = age
def __setattr__(self, name, value):
print("the method __setattr__ is running")
# self.name = value # 使用此方法赋值会出现无限循环的bug
'''以下代码运行会有死循环bug,故不运行'''
# demo = Demo('sh', 24)
# demo.name = "haha"
the method __setattr__ is running
the method __setattr__ is running
the method __setattr__ is running
the method __setattr__ is running
the method __setattr__ is running
the method __setattr__ is running
the method __setattr__ is running
---------------------------------------------------------------------------
RecursionError Traceback (most recent call last)
<ipython-input-6-4b033e0da789> in <module>
12
13
---> 14 demo = Demo('sh', 24)
15 try:
16 start_time = time()
<ipython-input-6-4b033e0da789> in __init__(self, name, age)
4
5 def __init__(self, name, age):
----> 6 self.name = name
7 self.age = age
8
<ipython-input-6-4b033e0da789> in __setattr__(self, name, value)
9 def __setattr__(self, name, value):
10 print("the method __setattr__ is running")
---> 11 self.name = value # 使用此方法赋值会出现无限循环的bug
12
13
... last 1 frames repeated, from the frame below ...
<ipython-input-6-4b033e0da789> in __setattr__(self, name, value)
9 def __setattr__(self, name, value):
10 print("the method __setattr__ is running")
---> 11 self.name = value # 使用此方法赋值会出现无限循环的bug
12
13
RecursionError: maximum recursion depth exceeded while calling a Python object
描述符
参考网址:
https://www.cnblogs.com/Lynnblog/p/9033455.html
链接已失效:网页404
https://blog.csdn.net/zhaoyun_zzz/article/details/82179481
描述符定义
'''
把实现了__get__()、__set__()和__delete__()中的其中任意一种方法的类称之为描述符,
描述符的本质是新式类,并且被代理的类(即应用描述符的类)也是新式类。
描述符的作用是用来代理一个类的属性,
需要注意的是描述符不能定义在类的构造函数中,只能定义为类的属性,它只属于类的,不属于实例。
'''
描述符种类及优先级
种类:
描述符分为数据描述符和非数据描述符。
数据描述符:至少实现了
__get__()
和
__set__()
方法
非数据描述符:没有实现
__set__()
分类的原因是在访问属性时的搜索顺序上:
获取一个属性的时候:
优先级:
搜索链(或者优先链)的顺序:数据描述符>实体属性(存储在实体的dict中)>非数据描述符
解释:
-
首先,看这个属性是不是一个数据描述符,
如果是,就直接执行描述符的
,并返回值。__get__()
- 其次,如果这个属性不是数据描述符,那就按常规去从
__dict__
里面取属性,
如果
__dict__
里面还没有,但这是一个非数据描述符,
则执行非数据描述符的
方法,并返回。__get__()
- 最后,找不到的属性触发
执行__getattr__()
而设置一个属性的值时,访问的顺序又有所不同
优先级:
类属性 > 数据描述符 > 实例属性 > 非数据描述符 > 找不到的属性触发
__getattr__()
三种方法的调用及参数解释:调用一个属性时,触发
__get__(self, instance, owner):
:为一个属性赋值时,触发
__set__(self, instance, value)
__delete__(self, instance)
:采用del删除属性时,触发
参数解释:
self: 是定义描述符的对象,不是使用描述符类的对象
instance: 这才是使用描述符类的对象
owner: 是instance的类(定义使用描述符的实例的类)
value: 是instance的值(使用描述符的实例的值)
# 对参数解释的例子
class Desc(object):
def __get__(self, instance, owner):
print("__get__...")
print("self : \t", self) # self : <__main__.Desc object at 0x7f957c069470>
print("instance : \t", instance) # instance : <__main__.TestDesc object at 0x7f957c069400>
print("owner : \t", owner) # owner : <class '__main__.TestDesc'>
print('='*40, "\n")
def __set__(self, instance, value):
print('__set__...')
print("self : \t\t", self)
print("instance : \t", instance)
print("value : \t", value)
print('='*40, "\n")
class TestDesc(object):
x = Desc()
#以下为测试代码
t = TestDesc()
t.x = 34
# print(t.x)
__set__...
self : <__main__.Desc object at 0x7fca10f46dd8>
instance : <__main__.TestDesc object at 0x7fca10f46e48>
value : 34
========================================
类属性>数据描述符
class Descriptors(object):
'''数据描述符'''
def __get__(self, instance, owner):
print('执行Descriptors的__get__()')
def __set__(self, instance, value):
print('执行Descriptors的__set__()')
def __delete__(self, instance):
print('执行Descriptors的__delete__()')
class DoDesc(object):
# 使用描述符
useDesc = Descriptors()
# test
DoDesc.useDesc # 执行描述符的get内置属性
print(DoDesc.__dict__) # 此时name显示的是描述符的对象
DoDesc.useDesc = 'TEST' # 未执行set内置属性
print(DoDesc.useDesc) # TEST
del DoDesc.useDesc # 未执行delete内置属性
# print(DoDesc.useDesc) # 报错,因为属性被删除了,无法找到
执行Descriptors的__get__()
{'__module__': '__main__', 'useDesc': <__main__.Descriptors object at 0x7f957c0424a8>, '__dict__': <attribute '__dict__' of 'DoDesc' objects>, '__weakref__': <attribute '__weakref__' of 'DoDesc' objects>, '__doc__': None}
TEST
数据描述符>实例属性
数据描述符的优先级大于实例属性的优先级,
此时实例属性name被数据描述符所覆盖,
而price没有描述符代理,所以它仍然是实例属性。
class Descriptors:
"""
数据描述符
"""
def __get__(self, instance, owner):
print("执行Descriptors的get")
def __set__(self, instance, value):
print("执行Descriptors的set")
def __delete__(self, instance):
print("执行Descriptors的delete")
class Light:
#使用描述符
name = Descriptors()
def __init__(self, name, price):
self.name = name
self.price = price
#使用类的实例对象来测试
light = Light("电灯泡",60) #执行描述符的set内置属性
light.name #执行描述符的get内置属性
print(light.__dict__) #查看实例的字典,不存在name
print(Light.__dict__) #查看类的字典,存在name(为描述符的对象)
del light.name #执行描述符的delete内置属性
执行Descriptors的set
执行Descriptors的get
{'price': 60}
{'__module__': '__main__', 'name': <__main__.Descriptors object at 0x7f957c05a0b8>, '__init__': <function Light.__init__ at 0x7f957572a1e0>, '__dict__': <attribute '__dict__' of 'Light' objects>, '__weakref__': <attribute '__weakref__' of 'Light' objects>, '__doc__': None}
执行Descriptors的delete
实例属性>非数据描述符
实例属性中使用了非数据描述符,就不能对其进行复制操作。
可见非数据描述符应该应用于不需要设置值的属性或者函数中。
class Descriptors:
"""
非数据描述符
"""
def __get__(self, instance, owner):
print("执行Descriptors的set")
def __delete__(self, instance):
print("执行Descriptors的delete")
class Light:
#使用描述符
name = Descriptors()
def __init__(self, name, price):
self.name = name
self.price = price
#测试
light = Light("电灯泡",60) #报错,描述符中没有__set__()方法
---------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-28-437b7a47917d> in <module>
18
19 #测试
---> 20 light = Light("电灯泡",60) #报错,描述符中没有__set__()方法
<ipython-input-28-437b7a47917d> in __init__(self, name, price)
14
15 def __init__(self, name, price):
---> 16 self.name = name
17 self.price = price
18
AttributeError: __set__
'''
在该类中并没有set方法,所以该类是一个非数据描述符,
Python中一切皆对象,函数也是一个对象,
既然是对象那也可以是类实例化所得到的结果。
函数在类中本身也是一种属性(函数属性),
描述符在应用的时候也是被实例化为一个属性。
'''
class Descriptors:
"""
非数据描述符
"""
def func(self):
print("世界的变化真快!近日00后都已经开始在街头打小三了")
d = Descriptors()
d.func()
print(hasattr(Descriptors.func,"__set__")) #False
print(hasattr(Descriptors.func,"__get__")) #True
print(hasattr(Descriptors.func,"__delete__")) #False
d.func = "函数也是属性,也可以赋值,没毛病"
print('print: ',d.func)
del d.func
d.func
世界的变化真快!近日00后都已经开始在街头打小三了
False
True
False
print: 函数也是属性,也可以赋值,没毛病
<bound method Descriptors.func of <__main__.Descriptors object at 0x7f957c0247f0>>
描述符使用注意点
'''
描述符必须定义在类层次上,否则Python无法自动调用设置的__get__()和__set__()
访问类层次上的描述符可以自动调用__get__(),但是访问实例层次上的描述符只会返回描述符本身
描述符在所有的实例间共享数据(描述符是类属性的一种)
'''
# 使用注意点 例子
class Desc(object):
def __init__(self, name):
self.name = name
def __get__(self, instance, owner):
print("__get__...")
print('name = ', self.name)
print('=' * 40, "\n")
class TestDesc(object):
x = Desc('x')
def __init__(self):
self.y = Desc('y')
# 以下为测试代码
t = TestDesc()
t.x # 相当于默认值
print(t.__dict__)
print(t.y) # 在__init__中对描述符有了新的赋值
__get__...
name = x
========================================
{'y': <__main__.Desc object at 0x7f957c024860>}
<__main__.Desc object at 0x7f957c024860>
描述符使用
常使用的@classmethod、@staticmethd、@property、甚至是__slots__等属性都是通过描述符来实现的。
# 1. 模拟 @classmethod
class Imitate_classmethod(object):
'''使用描述符模拟@classmethod'''
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
# 对传入的函数加工,并返回加工后的函数
def maching_func(*args, **kwargs):
print('函数加工后,返回实例的类')
return self.func(owner, *args, **kwargs)
return maching_func
class Test(object):
msg = '这是一个对描述符模拟classmethod的测试'
# 这里是一个类装饰器
@Imitate_classmethod
def about(cls):
print('print: ', cls.msg)
@Imitate_classmethod
def about_var(cls, value):
print('pirnt: {}, 传入的值为:{}'.format(cls.msg, value))
# test
t = Test()
Test.about()
Test.about_var(666)
print('\n下面是Test实例的输出:\n')
t.about()
t.about_var(777)
函数加工后,返回实例的类
print: 这是一个对描述符模拟classmethod的测试
函数加工后,返回实例的类
pirnt: 这是一个对描述符模拟classmethod的测试, 传入的值为:666
下面是Test实例的输出:
函数加工后,返回实例的类
print: 这是一个对描述符模拟classmethod的测试
函数加工后,返回实例的类
pirnt: 这是一个对描述符模拟classmethod的测试, 传入的值为:777
'''
2. 模拟 @staticmethod
staticmethod方法与classmethod方法的区别在于:
classmethod方法在使用需要传进一个类的引用作为参数。
而staticmethod则不用。
'''
# 例子
class Imitate_staticmethod:
'''
使用描述符模拟@staticmethod
'''
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
#对传进函数进行加工,最后返回该函数
def machining_func(*args, **kwargs):
print("函数加工处理后,返回实例的类")
return self.func(*args, **kwargs)
return machining_func
class Test:
@Imitate_staticmethod
def static_func(*args):
print("您输入的是:",*args)
#测试
Test.static_func("柠檬","香蕉")
test = Test()
test.static_func(110, 112)
函数加工处理后,返回实例的类
您输入的是: 柠檬 香蕉
函数加工处理后,返回实例的类
您输入的是: 110 112
'''
3. 模拟 @property
在下面的代码中,
将描述符的回调结果存入对象字典中的好处是以后再执行函数时就不会每一次都触发描述的运行,从而提高程序的效率。
这样,我们有再执行同样的函数时,解释器会先检索对象的字典,
如果字典存在上次执行结果的值,那就不用触发描述符的运行了。
在这个实验中必须强调的一点是描述符的优先级,
我们想让程序的描述符不能覆盖实例属性就必须使用非数据描述符。所以因需求不同,描述符的优先级也不同。
'''
# 例子
class Imitate_property:
'''使用描述符模拟property'''
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
if instance is None:
return self
#回调传入的函数,将运行结果保存在变量res中
res = self.func(instance)
#为函数名func.__name__设置一个值res后存入对象的字典中
setattr(instance, self.func.__name__, res)
return res
class Test:
def __init__(self, value):
self.value = value
@Imitate_property
def function(self):
return self.value**2
test = Test(2)
print(test.function) #输出:4
print(test.__dict__) #输出:{'value': 2, 'function': 4}
print(test.function) #输出:4
print(test.function) #输出:4
print(test.__dict__) #输出:{'value': 2, 'function': 4}
4
{'value': 2, 'function': 4}
4
4
{'value': 2, 'function': 4}