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

类定义

  1. class Cat:
  2. # 属性
  3. # 方法
  4. def eat(self):
  5. print('吃鱼')
  6. def drink(self):
  7. print('喝酸奶')
  8. 对象名1 = 类名()
  9. 对象名2 = 类名()
  10. 对象名3 = 类名()

方法

实例方法

类对象可以调用实例方法,实例方法的第一个参数一定是self
实例可以直接添加实例属性,tom.name = '汤姆',实例方法也能调用添加的实例属性

类属性

类属性就是类对象所拥有的属性,它被所有类对象的实例对象所共有,在内存中只存在一个副本

  1. class People(object):
  2. name = 'Tom' # 公有的类属性
  3. __age = 12 # 私有的类属性
  4. p = People()
  5. print(p.name) # 正确
  6. print(People.name) # 正确
  7. print(p.__age) # 错误,不能在类外通过实例对象访问私有的类属性
  8. print(People.__age) # 错误,不能在类外通过类对象访问私有的类属性
  1. class People(object):
  2. address = '山东' # 类属性
  3. def __init__(self):
  4. self.name = 'xiaowang' # 实例属性
  5. self.age = 20 # 实例属性
  6. p = People()
  7. p.age = 12 # 实例属性
  8. print(p.address) # 正确
  9. print(p.name) # 正确
  10. print(p.age) # 正确
  11. print(People.address) # 正确
  12. print(People.name) # 错误
  13. print(People.age) # 错误

如果需要在类外修改类属性,必须通过类对象去引用然后进行修改。如果通过实例对象去引用,会产生一个同名的实例属性,这种方式修改的是实例属性,不会影响到类属性,并且之后如果通过实例对象去引用该名称的属性,实例属性会强制屏蔽掉类属性,即引用的是实例属性,除非删除了该实例属性

它们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同,

  • 实例属性属于对象
  • 类属性属于类

image.png

  • 类属性在内存中只保存一份
  • 实例属性在每个对象中都要保存一份

python万物皆对象
三种方法在内存中都归属于类,区别在于调用方式不同。

  • 实例方法:由对象调用;至少一个self参数;执行实例方法时,自动将调用该方法的对象赋值给self;
  • 类方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类赋值给cls;
  • 静态方法:由类调用;无默认参数;

    类方法

    类对象所拥有的方法,需要用修饰器@classmethod来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以cls作为第一个参数(当然可以用其他名称的变量作为其第一个参数,但是大部分人都习惯以’cls’作为第一个参数的名字,就最好用’cls’了),能够通过实例对象和类对象去访问
  1. 从类方法和实例方法以及静态方法的定义形式就可以看出来,类方法的第一个参数是类对象cls,那么通过cls引用的必定是类对象的属性和方法;
  2. 实例方法的第一个参数是实例对象self,那么通过self引用的可能是类属性、也有可能是实例属性(这个需要具体分析),不过在存在相同名称的类属性和实例属性的情况下,实例属性优先级更高。
  3. 静态方法中不需要额外定义参数,因此在静态方法中引用类属性的话,必须通过类实例对象来引用 ```python class People(object): country = ‘china’

    类方法,用classmethod来进行修饰

    @classmethod def get_country(cls):

    1. 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())

魔法方法(属性,内建属性)

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

常用专有属性 说明 触发方式
init 构造初始化函数 创建实例后,赋值时使用,在new后自动调用
new 生成实例所需属性 创建实例时
class 实例所在的类 实例.class
str 实例字符串表示,可读性 print(类实例)如没实现,使用repr结果
repr 实例字符串表示,准确性 类实例 回车 或者 print(repr(类实例))
del 析构(对象被删除前做清理工作) del 实例后,如果对象引用计数为0,则自动调用
dict 实例自定义属性 vars(实例.dict)
doc 类文档,子类不继承 help(类或实例)
getattribute 属性访问拦截器 访问实例属性时
bases 类的所有父类构成元素 类名.bases
  • 在python中方法名如果是xxxx()的,那么就有特殊的功能,因此叫做“魔法”方法
  • 内建属性,往往是指我们,在使用类时,可以直接使用的那些功能,例如newinit

    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
    
  1. 在类内部获取 属性 和 实例方法,通过self获取;
  2. 在类外部获取 属性 和 实例方法,通过对象名获取。
  3. 如果一个类有多个对象,每个对象的属性是各自保存的,都有各自独立的地址;
  4. 但是实例方法是所有对象共享的,只占用一份内存空间。类会通过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返回的实例,initnew的基础上可以完成一些其它初始化的动作,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

  • module 表示当前操作的对象在那个模块
  • class 表示当前操作的对象的类是什么 ```python

    -- coding:utf-8 --

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': '山西'}

getitemsetitemdelitem

  • 用于索引操作,如字典。以上分别表示获取、设置、删除数据 ```python

    -- coding:utf-8 --

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

class的一个内置属性,表示类的类型,返回 ;
image.png

getattr

https://blog.updown.world/articles/python/pythonbook2/006%E6%8F%8F%E8%BF%B0%E7%AC%A6/01getattr%E5%92%8Cgetattribute.html

如果访问的属性存在,就不会调用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

getattributegetattr都是对访问属性时的特殊操作

  • getattr只针对未定义属性的调用
  • getattribute针对所有的属性运行

当同时定义getattributegetattr时,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装饰器

参考:https://blog.updown.world/articles/python/pythonbook2/04-%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E6%8F%90%E9%AB%982/04-property%E5%B1%9E%E6%80%A72.html

一种用起来像是使用的实例属性一样的特殊属性,可以对应于某个方法
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 方法

image.png

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(最常用)

  • 调用父类的方法,父类如果没有会再去父类的父类里找

    子类继承父类属性

  • 属性也像方法一样可以被继承使用

  • 子类也可以对继承的属性进行修改
  • 子类不能用super()来调用父类属性

    多继承

  • 注意:如果多个父类中有同名的 属性和方法,则默认使用第一个父类的属性和方法(根据类的魔法属性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
"""

元类

参考:https://blog.updown.world/articles/python/pythonbook2/03-Python%E9%AB%98%E7%BA%A73/01-%E5%85%83%E7%B1%BB.html

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…
    ...省略...
  1. Foo中有metaclass这个属性吗?如果是,Python会通过metaclass创建一个名字为Foo的类(对象)
  2. 如果Python没有找到metaclass,它会继续在Bar(父类)中寻找metaclass属性,并尝试做和前面同样的操作。
  3. 如果Python在任何父类中都找不到metaclass,它就会在模块层次中去寻找metaclass,并尝试做同样的操作。
  4. 如果还是找不到metaclass,Python就会用内置的type来创建这个类对象。
  5. 你可以在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等属性都是通过描述符来实现的