- OO概念
- 类构成
- 类定义
- 方法
- 类方法,用classmethod来进行修饰
- print(tom) # 如果没有用str方法,就打印对象地址 16进制 <__main__.Cat object at 0x000001F921D88A58>
- -- coding:utf-8 --
- -- coding:utf-8 --
- property装饰器
- ############### 定义
- 定义property属性
- ############### 调用
- coding=utf-8
- coding=utf-8
- python3的用法
- python2的用法
- class Foo(object):
- metaclass = UpperAttrMetaClass
- bar = ‘bip’
- 输出: False
- 输出:True
- 输出:’bip’
author: UpDown published: True created: 2022年5月10日11点33分 tags: Done version: Outer
参考:https://blog.updown.world/articles/python/pythonbook/file/6/section.1.html
OO概念
- 面向过程:根据业务逻辑从上到下写代码
面向对象:将数据与函数绑定到一起,进行封装,这样能够更快速的开发程序,减少了重复代码的重写过程
类构成
类(Class) 由3个部分构成
类的名称:类名
- 类的属性:一组数据
- 类的方法:允许对进行操作的方法 (行为)
Python3中定义的类都是新式类的,无论是否写明一个类继承object,都会间接或直接继承object
类定义
class Cat:
# 属性
# 方法
def eat(self):
print('吃鱼')
def drink(self):
print('喝酸奶')
对象名1 = 类名()
对象名2 = 类名()
对象名3 = 类名()
方法
实例方法
类对象可以调用实例方法,实例方法的第一个参数一定是self
实例可以直接添加实例属性,tom.name = '汤姆'
,实例方法也能调用添加的实例属性
类属性
类属性就是类对象所拥有的属性,它被所有类对象的实例对象所共有,在内存中只存在一个副本
class People(object):
name = 'Tom' # 公有的类属性
__age = 12 # 私有的类属性
p = People()
print(p.name) # 正确
print(People.name) # 正确
print(p.__age) # 错误,不能在类外通过实例对象访问私有的类属性
print(People.__age) # 错误,不能在类外通过类对象访问私有的类属性
class People(object):
address = '山东' # 类属性
def __init__(self):
self.name = 'xiaowang' # 实例属性
self.age = 20 # 实例属性
p = People()
p.age = 12 # 实例属性
print(p.address) # 正确
print(p.name) # 正确
print(p.age) # 正确
print(People.address) # 正确
print(People.name) # 错误
print(People.age) # 错误
如果需要在类外修改类属性,必须通过类对象去引用然后进行修改。如果通过实例对象去引用,会产生一个同名的实例属性,这种方式修改的是实例属性,不会影响到类属性,并且之后如果通过实例对象去引用该名称的属性,实例属性会强制屏蔽掉类属性,即引用的是实例属性,除非删除了该实例属性
它们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同,
- 实例属性属于对象
- 类属性属于类
- 类属性在内存中只保存一份
- 实例属性在每个对象中都要保存一份
python万物皆对象
三种方法在内存中都归属于类,区别在于调用方式不同。
- 实例方法:由对象调用;至少一个self参数;执行实例方法时,自动将调用该方法的对象赋值给self;
- 类方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类赋值给cls;
- 静态方法:由类调用;无默认参数;
类方法
类对象所拥有的方法,需要用修饰器@classmethod来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以cls作为第一个参数(当然可以用其他名称的变量作为其第一个参数,但是大部分人都习惯以’cls’作为第一个参数的名字,就最好用’cls’了),能够通过实例对象和类对象去访问
- 从类方法和实例方法以及静态方法的定义形式就可以看出来,类方法的第一个参数是类对象cls,那么通过cls引用的必定是类对象的属性和方法;
- 实例方法的第一个参数是实例对象self,那么通过self引用的可能是类属性、也有可能是实例属性(这个需要具体分析),不过在存在相同名称的类属性和实例属性的情况下,实例属性优先级更高。
静态方法中不需要额外定义参数,因此在静态方法中引用类属性的话,必须通过类实例对象来引用 ```python class People(object): country = ‘china’
类方法,用classmethod来进行修饰
@classmethod def get_country(cls):
return cls.country
p = People() print(p.get_country()) #可以用过实例对象引用 print(People.get_country()) #可以通过类对象引用
类方法还有一个用途就是可以对类属性进行修改
<a name="DNmzk"></a>
#### 静态方法
通过修饰器@staticmethod来进行修饰,静态方法不需要多定义参数,可以通过对象和类来访问。
```python
class People(object):
country = 'china'
@staticmethod
#静态方法
def get_country():
return People.country
p = People()
# 通过对象访问静态方法
p.get_contry()
# 通过类访问静态方法
print(People.get_country())
魔法方法(属性,内建属性)
常用专有属性 | 说明 | 触发方式 |
---|---|---|
init | 构造初始化函数 | 创建实例后,赋值时使用,在new后自动调用 |
new | 生成实例所需属性 | 创建实例时 |
class | 实例所在的类 | 实例.class |
str | 实例字符串表示,可读性 | print(类实例)如没实现,使用repr结果 |
repr | 实例字符串表示,准确性 | 类实例 回车 或者 print(repr(类实例)) |
del | 析构(对象被删除前做清理工作) | del 实例后,如果对象引用计数为0,则自动调用 |
dict | 实例自定义属性 | vars(实例.dict) |
doc | 类文档,子类不继承 | help(类或实例) |
getattribute | 属性访问拦截器 | 访问实例属性时 |
bases | 类的所有父类构成元素 | 类名.bases |
- 在python中方法名如果是xxxx()的,那么就有特殊的功能,因此叫做“魔法”方法
内建属性,往往是指我们,在使用类时,可以直接使用的那些功能,例如new、init等
init()
init()方法,在创建一个对象时默认被调用,不需要手动调用
- init(self)中的self参数,不需要开发者传递,python解释器会自动把当前的对象引用传递过去。
class Cat: # 属性 def __init__(self, new_name, new_age): print('------init--------') self.name = new_name self.age = new_age
- 在类内部获取 属性 和 实例方法,通过self获取;
- 在类外部获取 属性 和 实例方法,通过对象名获取。
- 如果一个类有多个对象,每个对象的属性是各自保存的,都有各自独立的地址;
- 但是实例方法是所有对象共享的,只占用一份内存空间。类会通过self来判断是哪个对象调用了实例方法。
str()
- 当使用print输出对象的时候,默认打印对象的内存地址。如果类定义了str(self)方法,那么就会打印从在这个方法中 return 的数据
- str方法通常返回一个字符串,作为这个对象的描述信息
```python
class Cat:
def str(self):print('名字%s----年龄:%s' % (self.name, self.age))
print(tom) # 如果没有用str方法,就打印对象地址 16进制 <__main__.Cat object at 0x000001F921D88A58>
print(tom) #如果写了str方法,就打印方法的返回内容 名字汤姆——年龄:30
<a name="mmi9v"></a>
#### __del__()
创建对象后,python解释器默认调用__init__()方法;<br />当删除对象时,python解释器也会默认调用一个方法,这个方法为__del__()方法
```python
class Hero(object):
# 当对象被删除时,会自动被调用
def __del__(self):
print("__del__方法被调用")
print("%s 被 GM 干掉了..." % self.name)
- 当有变量保存了一个对象的引用时,此对象的引用计数就会加1;
当使用del() 删除变量指向的对象时,则会减少对象的引用计数。如果对象的引用计数不为1,那么会让这个对象的引用计数减1,当对象的引用计数为0的时候,则对象才会被真正删除(内存被回收)。
new()
https://blog.updown.world/articles/python/pythonbook/file/6/section.16.html
new至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供
- new必须要有返回值,返回实例化出来的实例,这点在自己实现new时要特别注意,可以return父类new出来的实例,或者直接是object的new出来的实例
- init有一个参数self,就是这个new返回的实例,init在new的基础上可以完成一些其它初始化的动作,init不需要返回值
我们可以将类比作制造商,new方法就是前期的原材料购买环节,init方法就是在有原材料的基础上,加工,初始化商品环节 ```python class A(object): def init(self):
print("这是 init 方法")
def new(cls):
print("这是 new 方法") return object.__new__(cls)
a = A()
<a name="cWhGL"></a>
#### __call__()
> [http://c.biancheng.net/view/2380.html](http://c.biancheng.net/view/2380.html)
__call__()。该方法的功能类似于在类中重载 () 运算符,使得类实例对象可以像调用普通函数那样,以“对象名()”的形式使用
<a name="rhzK5"></a>
#### __getattribute__
> [https://blog.updown.world/articles/python/pythonbook2/03-%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E6%8F%90%E9%AB%981/04-%E5%86%85%E5%BB%BA%E5%B1%9E%E6%80%A7.html](https://blog.updown.world/articles/python/pythonbook2/03-%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E6%8F%90%E9%AB%981/04-%E5%86%85%E5%BB%BA%E5%B1%9E%E6%80%A7.html)
<a name="PKKFH"></a>
#### __doc__
- 表示类的描述信息
```python
class Foo:
""" 描述类信息,这是用于看片的神奇 """
def func(self):
pass
print(Foo.__doc__)
#输出:类的描述信息
"""
描述类信息,这是用于看片的神奇
"""
module和_class
class Person(object): def init(self): self.name = ‘laowang’
```python
from test import Person
obj = Person()
print(obj.__module__) # 输出 test 即:输出模块
print(obj.__class__) # 输出 test.Person 即:输出类
"""
test
<class 'test.Person'>
"""
dict
- 类或对象中的所有属性
类的实例属性属于对象;类中的类属性和方法等属于类,即:
class Province(object):
country = 'China'
def __init__(self, name, count):
self.name = name
self.count = count
def func(self, *args, **kwargs):
print('func')
# 获取类的属性,即:类属性、方法、
print(Province.__dict__)
# 输出:{'__dict__': <attribute '__dict__' of 'Province' objects>, '__module__': '__main__', 'country': 'China', '__doc__': None, '__weakref__': <attribute '__weakref__' of 'Province' objects>, 'func': <function Province.func at 0x101897950>, '__init__': <function Province.__init__ at 0x1018978c8>}
obj1 = Province('山东', 10000)
print(obj1.__dict__)
# 获取 对象obj1 的属性
# 输出:{'count': 10000, 'name': '山东'}
obj2 = Province('山西', 20000)
print(obj2.__dict__)
# 获取 对象obj1 的属性
# 输出:{'count': 20000, 'name': '山西'}
getitem、setitem、delitem
class Foo(object):
def __getitem__(self, key):
print('__getitem__', key)
def __setitem__(self, key, value):
print('__setitem__', key, value)
def __delitem__(self, key):
print('__delitem__', key)
obj = Foo()
result = obj[‘k1’] # 自动触发执行 getitem obj[‘k2’] = ‘laowang’ # 自动触发执行 setitem del obj[‘k1’] # 自动触发执行 delitem
<a name="eV7to"></a>
#### __slots__
定义一个特殊的__slots__属性,来限制该类创建的实例对象可以添加的属性
- 使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的
```python
class Person(object):
__slots__ = ("name", "age")
p1 = Person()
p1.name = "teacher" # 可以执行
p1.age = 20 # 可以执行
p1.score = 100 # 执行失败
class
https://blog.csdn.net/The_Time_Runner/article/details/99730676
getattr
如果访问的属性存在,就不会调用getattr方法。
这个属性的存在,包括类属性和实例属性
class ClassA:
x = 'a'
def __init__(self):
self.y = 'b'
def __getattr__(self, item):
return '__getattr__'
if __name__ == '__main__':
a = ClassA()
print(a.x)# 输出结果 a
# 使用实例直接访问实例存在的实例属性时,不会调用__getattr__方法
print(a.y) # 输出结果 b
# 使用实例直接访问实例不存在的实例属性时,会调用__getattr__方法
print(a.z) # 输出结果 __getattr__
getattribute
getattribute和getattr都是对访问属性时的特殊操作
- getattr只针对未定义属性的调用
- getattribute针对所有的属性运行
当同时定义getattribute和getattr时,getattr方法不会再被调用,除非显示调用getattr方法或引发AttributeError异常。
示例代码(getattr方法不会再被调用):
class ClassA:
x = 'a'
def __init__(self):
self.y = 'b'
def __getattribute__(self, item):
return '__getattribute__'
if __name__ == '__main__':
a = ClassA()
# 使用实例直接访问存在的类属性时,会调用__getattribute__方法
print(a.x)# 输出结果 __getattribute__
# 使用实例直接访问实例存在的实例属性时,会调用__getattribute__方法
print(a.y) # 输出结果 __getattribute__
# 使用实例直接访问实例不存在的实例属性时,也会调用__getattribute__方法
print(a.z) # 输出结果 __getattribute__
"""
__getattribute__
__getattribute__
__getattribute__
"""
注意
由于getattr只针对未定义属性的调用,所以它可以在自己的代码中自由地获取其他属性,而getattribute针对所有的属性运行,因此要十分注意避免在访问其他属性时,再次调用自身的递归循环。
当在getattribute代码块中,再次执行属性的获取操作时,会再次触发getattribute方法的调用,代码将会陷入无限递归,直到Python递归深度限制(重载setter方法也会有这个问题)
class ClassA:
x = 'a'
def __getattribute__(self, item):
print('__getattribute__')
return self.item
if __name__ == '__main__':
a = ClassA()
a.x
class ClassA:
x = 'a'
def __getattribute__(self, item):
print('__getattribute__')
return super().__getattribute__(item)
if __name__ == '__main__':
a = ClassA()
print(a.x)
其实如果不重写getattribute,就是调用父类的 也就相当于是super().getattribute(item)
property装饰器
一种用起来像是使用的实例属性一样的特殊属性,可以对应于某个方法
Python的property属性的功能是:property属性内部进行一系列的逻辑计算,最终将计算结果返回
- 定义时,在实例方法的基础上添加 @property 装饰器;并且仅有一个self参数
调用时,无需括号
方法:foo_obj.func() property属性:foo_obj.prop
```python
############### 定义
class Foo: def func(self):
print('func')
定义property属性
@property def prop(self):
print('prop') return 'prop 返回'
############### 调用
foo_obj = Foo() foo_obj.func() # 调用实例方法 re = foo_obj.prop # 调用property属性 print(re)
<a name="tq5KV"></a>
### ![image.png](https://cdn.nlark.com/yuque/0/2022/png/22442921/1652177150259-a6764be1-1fd8-4532-909c-9048fdf4a17f.png#clientId=u62a09127-947d-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=268&id=u51a9320d&name=image.png&originHeight=498&originWidth=1004&originalType=binary&ratio=1&rotation=0&showTitle=false&size=131012&status=done&style=none&taskId=ucb5b5f84-507f-4564-8e08-402bdb4c857&title=&width=540.3333740234375)
<a name="STNA8"></a>
#### 三种@property装饰器
```python
class Goods:
"""python3中默认继承object类
以python2、3执行此程序的结果不同,因为只有在python3中才有@xxx.setter @xxx.deleter
"""
@property
def price(self):
print('@property')
return '1000'
@price.setter
def price(self, value):
print('@price.setter')
@price.deleter
def price(self):
print('@price.deleter')
obj = Goods()
print(obj.price) # 自动执行 @property 修饰的 price 方法,并获取方法的返回值
obj.price = 123 # 自动执行 @price.setter 修饰的 price 方法,并将 123 赋值给方法的参数
del obj.price # 自动执行 @price.deleter 修饰的 price 方法
property类方法
property方法中有个四个参数
- 第一个参数是方法名,调用 对象.属性 时自动触发执行方法
- 第二个参数是方法名,调用 对象.属性 = XXX 时自动触发执行方法
- 第三个参数是方法名,调用 del 对象.属性 时自动触发执行方法
第四个参数是字符串,调用 对象.属性.doc ,此参数是该属性的描述信息 ```python
coding=utf-8
class Foo(object): def get_bar(self):
print("getter...") return 'python'
def set_bar(self, value):
"""必须两个参数""" print("setter...") return 'set value' + value
def del_bar(self):
print("deleter..del_bar.")
BAR = property(get_bar, set_bar, del_bar, “description…描述”)
obj = Foo()
print(obj.BAR) # 自动调用第一个参数中定义的方法:getbar print(‘-‘ 50) obj.BAR = “halon” # 自动调用第二个参数中定义的方法:set_bar方法,并将“halon”当作参数传入 print(obj.BAR) # 自动调用第一个参数中定义的方法:get_bar print(‘-‘ 50) desc = Foo.BAR._doc # 自动获取第四个参数中设置的值:description… print(desc) print(‘-‘ * 50) del obj.BAR # 自动调用第三个参数中定义的方法:del_bar方法
<a name="gpMWh"></a>
### 私有属性和私有方法
<a name="K6hNv"></a>
#### 私有权限:在属性名和方法名 前面 加上两个下划线 __
- 类的私有属性 和 私有方法,都不能通过对象直接访问,但是可以在本类内部访问;
- 私有属性 和 私有方法 往往用来处理类的内部事情,不通过对象处理,起到安全作用。
- **私有方法和私有属性无法被继承**
```python
class Person:
def __init__(self):
# 私有属性
self.__name = '天下第一大帅锅'
def my_name(self, password):
if password == password:
# 私有属性,可以在类的内部使用
print(self.__name)
else:
print('不告诉你')
person = Person()
# print(person.__name) # 私有属性无发使用
person.my_name(123456) # 通过调用方法来使用到私用属性
class Person:
def __process_bug(self):
print('专业处理程序bug')
def process_bug(self, money):
if money > 100:
self.__process_bug()
else:
print('hehe')
person = Person()
# print(person.__process_bug)
print(person.process_bug(200))
继承
# 定义一个Master类 master精通, 大师的意思
class Master:
def __init__(self):
print('---Master--init---')
def yyz(self):
print("发出一招-----一阳指")
# 定义Prentice类,继承了 Master
class Apprentice(Master):
pass
wukong = Apprentice() # 创建子类实例对象
wukong.yyz() # 调用方法一阳指
- 虽然子类没有定义init方法初始化属性,也没有定义实例方法,但是父类有。所以只要创建子类的对象,就默认执行了那个继承过来的init方法
- 子类在继承的时候,在定义类时,小括号()中为父类的名字
- 父类的属性、方法,会被继承给子类
- 只有一个父类的继承,叫做单继承
- 子类可以调用自己创建的方法,也可以调用父类的方法
-
重写
子类写的方法和父类重名,就是对父类方法的重写
- 重写后,子类调用方法,就会执行自己方法里的内容
调用父类的方法
https://blog.updown.world/articles/python/pythonbook/file/6/section.7.1.4.html
1.父类名.父类方法(self)
#调用父类的方法1
2.super(本类名, self).父类方法()
#调用父类的方法2
3.super().父类方法()
##调用父类的方法3(最常用)
-
子类继承父类属性
属性也像方法一样可以被继承使用
- 子类也可以对继承的属性进行修改
-
多继承
注意:如果多个父类中有同名的 属性和方法,则默认使用第一个父类的属性和方法(根据类的魔法属性mro的顺序来查找)
- 多个父类中,不重名的属性和方法,不会有任何影响。
面试题
以下的代码的输出将是什么? 说出你的答案并解释。 ```python class Parent(object): x = 1
class Child1(Parent): pass
class Child2(Parent): pass
print(Parent.x, Child1.x, Child2.x) Child1.x = 2 print(Parent.x, Child1.x, Child2.x) Parent.x = 3 print(Parent.x, Child1.x, Child2.x)
“”” 1 1 1 1 2 1 3 2 3 “””
<a name="k2LeI"></a>
### 多态
所谓多态:定义时的类型和运行时的类型不一样,此时就成为多态 ,多态的概念是应用于Java和C#这一类强类型语言中,而Python崇尚“鸭子类型”。
> 鸭子类型:虽然我想要一只"鸭子",但是你给了我一只鸟。 但是只要这只鸟走路像鸭子,叫起来像鸭子,游泳也像鸭子,我就认为这是鸭子。
Python的多态,就是弱化类型,重点在于对象参数是否有指定的属性和方法,如果有就认定合适,而不关心对象的类型是否正确。<br />python是弱类型,即无论传递过来的是什么,obj变量都能够指向它,这也就没有所谓的多态了(弱化了这个概念)
> [http://c.biancheng.net/view/5833.html](http://c.biancheng.net/view/5833.html)
```python
class WhoSay:
def say(self,who):
who.say()
class CLanguage:
def say(self):
print("调用的是 Clanguage 类的say方法")
class CPython(CLanguage):
def say(self):
print("调用的是 CPython 类的say方法")
class CLinux(CLanguage):
def say(self):
print("调用的是 CLinux 类的say方法")
a = WhoSay()
#调用 CLanguage 类的 say() 方法
a.say(CLanguage())
#调用 CPython 类的 say() 方法
a.say(CPython())
#调用 CLinux 类的 say() 方法
a.say(CLinux())
"""
调用的是 Clanguage 类的say方法
调用的是 CPython 类的say方法
调用的是 CLinux 类的say方法
"""
封装
将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数据的源代码进行有机的结合,形成“类”,其中数据和函数都是类的成员。
这样隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别
单例模式
单例类:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类
单例模式:是一种设计模式 使用单例类来创建实例对象,只有一个实例,这种设计模式叫做单例模式
# 实例化一个单例
class HuangD(object):
__instance = None
__is_first = True
def __init__(self, name):
if HuangD.__is_first:
self.name = name
HuangD.__is_first = False
def __new__(cls, name):
# 如果类属性__instance的值为None,
# 那么就创建一个对象,并且赋值__instance为这个对象的引用,保证下次调用这个方法时
# 能够知道之前已经创建过对象了,这样就保证了只有1个对象
if not cls.__instance:
print('创建一个新对象')
cls.__instance = object.__new__(cls)
return cls.__instance
kangx01 = HuangD("康熙")
kangx02 = HuangD("康熙")
print(id(kangx01))
print(id(kangx02))
"""
创建一个新对象
2148226776800
2148226776800
"""
元类
type
函数type实际上是一个元类。type就是Python在背后用来创建所有类的元类
str是用来创建字符串对象的类,而int是用来创建整数对象的类。type就是创建类对象的类
type(类名, 由父类名称组成的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))
class A(object):
num = 100
def print_b(self):
print(self.num)
@staticmethod
def print_static():
print("----haha-----")
@classmethod
def print_class(cls):
print(cls.num)
B = type("B", (A,), {"print_b": print_b, "print_static": print_static, "print_class": print_class})
b = B()
b.print_b()
b.print_static()
b.print_class()
metaclass属性
你可以在定义一个类的时候为其添加metaclass属性。
thon做了如下的操作:
class Foo(object):
__metaclass__ = something…
...省略...
- Foo中有metaclass这个属性吗?如果是,Python会通过metaclass创建一个名字为Foo的类(对象)
- 如果Python没有找到metaclass,它会继续在Bar(父类)中寻找metaclass属性,并尝试做和前面同样的操作。
- 如果Python在任何父类中都找不到metaclass,它就会在模块层次中去寻找metaclass,并尝试做同样的操作。
- 如果还是找不到metaclass,Python就会用内置的type来创建这个类对象。
- 你可以在metaclass中放置些什么代码呢?答案就是:可以创建一个类的东西。那么什么可以用来创建一个类呢?type,或者任何使用到type或者子类化type的东东都可以。
```python
coding=utf-8
class UpperAttrMetaClass(type):
# __new__ 是在__init__之前被调用的特殊方法
# __new__是用来创建对象并返回之的方法
# 而__init__只是用来将传入的参数初始化给对象
# 你很少用到__new__,除非你希望能够控制对象的创建
# 这里,创建的对象是类,我们希望能够自定义它,所以我们这里改写__new__
# 如果你希望的话,你也可以在__init__中做些事情
# 还有一些高级的用法会涉及到改写__call__特殊方法,但是我们这里不用
def __new__(cls, class_name, class_parents, class_attr):
# 遍历属性字典,把不是__开头的属性名字变为大写
new_attr = {}
for name, value in class_attr.items():
if not name.startswith("__"):
new_attr[name.upper()] = value
# 方法1:通过'type'来做类对象的创建
return type(class_name, class_parents, new_attr)
# 方法2:复用type.__new__方法
# 这就是基本的OOP编程,没什么魔法
# return type.__new__(cls, class_name, class_parents, new_attr)
python3的用法
class Foo(object, metaclass=UpperAttrMetaClass): bar = ‘bip’
python2的用法
class Foo(object):
metaclass = UpperAttrMetaClass
bar = ‘bip’
print(hasattr(Foo, ‘bar’))
输出: False
print(hasattr(Foo, ‘BAR’))
输出:True
f = Foo() print(f.BAR)
输出:’bip’
“元类就是深度的魔法,99%的用户应该根本不必为此操心。如果你想搞清楚究竟是否需要用到元类,那么你就不需要它。那些实际用到元类的人都非常清楚地知道他们需要做什么,而且根本不需要解释为什么要用元类。” —— Python界的领袖 Tim Peters
<a name="fOPMu"></a>
### 描述符
> [https://blog.updown.world/articles/python/pythonbook2/006%E6%8F%8F%E8%BF%B0%E7%AC%A6/01__getattr__%E5%92%8C__getattribute__.html](https://blog.updown.world/articles/python/pythonbook2/006%E6%8F%8F%E8%BF%B0%E7%AC%A6/01__getattr__%E5%92%8C__getattribute__.html)
__get__()、__set__()和__delete__()中的其中任意一种方法的新式类<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/22442921/1652183062026-83718602-59e1-4934-8530-afc4931dccd3.png#clientId=u934d6f69-1105-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=243&id=u1da034bc&name=image.png&originHeight=490&originWidth=901&originalType=binary&ratio=1&rotation=0&showTitle=false&size=97842&status=done&style=none&taskId=ua1700cf5-61fa-437b-bc7c-a35b54b05c0&title=&width=447.66668701171875)<br />上面代码中Foo的属性attr是类MyDescriptor的实例化对象,MyDescriptor实现了__get__()、__set__()和__delete__(),那么attr就是成为了描述符,注意attr必须用类属性形式写。<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/22442921/1652183156245-9bd3f60f-dd7d-483c-b02d-d538c8d321e9.png#clientId=u934d6f69-1105-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=227&id=uc2dfbe50&name=image.png&originHeight=457&originWidth=1077&originalType=url&ratio=1&rotation=0&showTitle=false&size=50953&status=done&style=none&taskId=u8303d37d-a387-4187-9fa4-2bd4b8f742d&title=&width=536)<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/22442921/1652183554173-dd59527e-c192-4b2b-97c8-b1eaeac5c381.png#clientId=u934d6f69-1105-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=326&id=ue165f420&name=image.png&originHeight=1449&originWidth=2789&originalType=url&ratio=1&rotation=0&showTitle=false&size=234274&status=done&style=none&taskId=ue63904b2-d214-4031-9f9f-262479b1b91&title=&width=628)
- 同时定义了__get__和__set__方法的描述符称为**数据描述符**
- 只定义了__get__的描述符称为**非数据描述符**
<a name="f5TO0"></a>
#### 调用顺序
- 程序会先查找 aa.__dict__['m'] 是否存在
- 不存在再到type(aa).__dict__['m']中查找
- 然后找type(aa)的父类
- 期间找到的是普通值就输出,如果找到的是一个描述符,则调用__get__方法
<a name="I6lzG"></a>
#### 参数
__get__方法里的instance, owner参数是什么<br />__set__里的instance, value是什么
```python
def __get__(self, instance, owner):
def __set__(self, instance, value):
- 我们看到了instance是Person的实例化对象p,owner就是Person这个类对象value就是设置属性时候传入的值
小总结
描述符是可以实现大部分Python类特性中最底层的数据结构的实现手段,我们常使用的@classmethod、@staticmethd、@property等属性都是通过描述符来实现的