python面向对象

语言分类

面向机器

机器容易理解的语言,是一些机器指令,代表有汇编语言

面向过程

做一件事情,按步骤实现,第一步做什么,第二步做什么,如果情况A出现怎么处理,情况B处理怎么处理。适用问题规模小,可以按步骤实现,代表C语言

面向对象OOP

5*

是一种认识世界、分析世界的方法论。将万事万物抽象成为各种对象。

适用问题规模大,复杂的情况,代表C++、Java、Python

类class是一个抽象的概念

对象object,实例instance,都是一个实体,是个具体的概念

数据类,对应属性;动作类,操作类,行为,对应方法,类是对数据和操作的封装。

类中方法也是属性

  • 生成器 迭代器 表达式

表达式又名解析式,用来构建list,set, dict, 比for循环效率高,使用列表表达式会立即生成新列表,占用大量的内存和cup;生成器的构建方式有生成器表达式,或者在函数中使用yield关键字,生成器是一个惰性对象,不会立即占用大量内存和cup,可以通过for循环和next方法获得里面的元素,生成器是一种迭代器。

面向对象3要素5*

1.封装

第一,将属性和方法封装, 也可以将对象封装到类当中

第二,将数据和操作适当暴露给客户,对外提供一些接口, 该隐藏的隐藏,该暴露的暴露,暴露的方法是公有方法,隐藏的方法有在属性和方法前面加一个下划线或者两个下划线(protected、私有)。

(暴露的方法有property中的getter,setter方法. 可以用装饰器实现,也可以自己写函数加到property实例中。)

2.继承

都是来自object(python3)类,object父类为None;分为单一继承和多继承,子可以直接调用或者重写的父类的属性和方法,尽量使用单一继承,少用多继承,多继承,OCP开闭原则(软件实现应该对扩展开放,对修改关闭)

(object无祖先,所有类的类型都是type)

单一继承

# 父类 超类 基类
# 子类 派生类
# 继承过程也叫派生过程
class Animal:
    def __init__(self, name):
        self._name = name

    def shout(self):
        print('{} , shouts'.format(self.__class__.__name__))
        # 在父类中,通过实例取得,子类的名字

class Cat(Animal): pass

c = Cat('tutu')
c.shout()
print(c.__class__.__base__)  #Animal
print(c.__class__.__bases__) #元组(Animal, )
print(c.__class__.__mro__) #通过属性获得mro,方法解析顺序,元组里面放的是类Cat Animal object
print(Cat.mro())  #  通过方法获得mro, 方法解析顺序,返回列表,同上
print(Animal.__subclasses__())  #Cat
print(int.__subclasses__())  #布尔型

# # object是根基类,没有父类,父类为None
# print(type(Cat)) type类型
# print(type(object)) type类型

多继承(少用)

python使用MRO(method resolution order 方法解析顺序),解决基类搜素顺序的问题,将非线性结构转化为线性搜索顺序。内部通过C3算法实现,采用深度优先原则。

多继承 :一个类继承自多个类,称为多继承
缺点:二义性
原则:OCP原则,多用继承,少修改
用途:复用,在子类上实现对基类的增强,实现多肽

3.多态

多肽就是继承加覆盖, B C两个类同时继承了A类,并重写了A类的一个方法,
同一套方法,在不同的子类上表现不同,这就是多肽。

(面向对象编程最灵活的地方,动态绑定,同一个方法在,不同的类型上

表现不同。例如继承自动物类的人类、猫类的操作'吃'方法不同)

python中的面向对象

定义:类名大驼峰(标识符),class关键字,定义后产生类对象,绑定到标识符上。

class ClassName:
    语句块
#实例化的真实过程,先调用了类的 obj = MyClass.__new__()
#然后调用了obj.__init__()方法,但是__init__()的返回值只能是None,
#内部做了优化所以,实例化之后返回一个对象,每次实例化都走一次__init__
#实例化与初始化
#先实例化再初始化
#__init__称为初始化,出厂配置,构造器,构造方法,dunder方法,第一参数必须是实例
#类属性与类方法
#类属性与类方法本质上都是类属性
#类方法method,就是函数,可以通过类标识符.函数名,访问这个属性
#点号,就是成员访问符

普通方法,类方法,静态方法

例子:

class Path:
    def __init__(self):
        self.name = 'tom'

    def method(self, m):  #普通方法,需要2个参数,类调用时不会注入第一参数,实例调用时,
        # 会将实例自身作为第一参数注入。
        print('普通方法:', self, m)

    @classmethod
    def class_method(cls, b):  #类方法,无论类调用还是实例调用,都会将类作为第一参数注入,第一参数默认是类,都不需要手动给值
        print('class method :', cls, b)

    @staticmethod  #(用的少)静态方法, 无论类调用还是实例调用,都不会注入第一参数
    def static_method(b, a):
        print('static method :', b, a)

# 普通方法例子
print(Path.method) #

类对象与实例对象的_class_ __name__ __dict__

__class__表示类型

class Person:
    height = 170

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def getage(self):
        print(self.name, __class__.__name__, self.__class__.__name__,type(self).__name__)
        # 后3种都可以取到类的__name__

pclass = Person
pinstance = Person('Tom', 18)

# __name__
pclass.__name__  # Person, 只有类可以取到名字,实例如果不定义取不到name

# __class__
# 如果直接在类中使用__class__,不通过点的方式,则表示当前类
pclass.__class__  # 类型是type  pclass.__base__父类为object
pinstance.__class__  # Person

# __dict___
pclass.__dict__  # {'height': 170 , '__init__':  ,'getage':,  '__doc__': }
pinstance.__dict__  # {'name': 'tom', 'age': 18}

print(*Person.__dict__.items(), sep='\n') #查看当前类的所有__dict__

函数与类中出现+=比较

class Person:
    height = 170

    def __init__(self, name, age):
        self.name = name
        self.age = age


p = Person('tom', 12)
p.height += 10  # 不会报错,不会走__init__,只会到类中去找height属性
p.height


def f():
    a = 10

    def f1():
        a += 1
        print(a)

    return f1


# f()()  报错

# 另一些例子
class Person:
    def __init__(self, name, age=18):
        self.name = name
        self.age = age

    def growup(self, i=1):
        self.age += i
        print(self.age)

    # 因为已经传入了一个i,所以不会报错
    def growup1(self, i=1):
        i += 1
        print('i', i)

    a = 1

    def growup2(self):
        __class__.a += 1
        print('a', __class__.a)

    # 因为没有传入了i,在函数内部赋值即定义,所以会报错
    b = 2

    def growup3(self):
        b += 1


p = Person('tom')
print(p.growup()) # 19
print(p.growup1()) # 2
print(p.growup2()) # 2
p.growup3()  # 会报错

访问控制

私有,公有,protected

  • self.__age = 1 私有属性,只能中使用_Person.__age, 当前类名加私有属性名,实例不可以访问, 想查看到_Person.__age,需要调用实例 .__dict__

    # 当存在于实例的字典当中时
    p.__dict__.get("_Person.__age")
    
    # 当存在于类的字典当中时
    Person.__dict__.get("_Person.__age")
    
  • _self._age = 1 protected保护的属性,名字还是_age, 可以访问和修改

    print(p._age)
    print(Person._height)  # 当实例的字典中存在_height属性时可以访问
    
  • self.age = 1 公有属性

    # 当age为类属性时
    print(Person.age)
    print(p.age)
    
  • 同样适用于类的方法,分为私用,保护,公有方法,因为
    method也是一种属性

继承中的访问控制

# 一下例子用来测试,正常代码不要这么写

class Animal:
    __COUNT = 100
    HEIGHT = 0

    def __init__(self, age, weight, height):
        self.__COUNT += 1
        self.age = age
        self.__weight = weight
        self.HEIGHT = height

    def eat(self):
        print('{}  eat'.format(self.__class__.__name__))

    def __getweight(self):
        print(self.__weight)

    @classmethod
    def showcount(cls):
        print(cls)
        print(cls.__dict__)
        print(cls.__COUNT)

    @classmethod
    def __showcount2(cls):
        print(cls.__COUNT)

    def showcount3(self):
        print(self.__COUNT)


class Cat(Animal):
    NAME = 'CAT'
    __COUNT = 200

# 测试(只是为了测试)
c = Cat(3, 5, 15)
c.eat()

# print(c.__COUNT)  #私有属性不可以通过实例直接访问访问
# CAT类的字典中_Cat__COUNT,访问方式_Cat__COUNT
# Animal类的字典中_Animal__COUNT ,访问方式_Animal__COUNT
# 实例的字典中没有这个属性,实例的字典中有_Animal__COUNT,访问方式_Animal__COUNT
print(Cat._Cat__COUNT)  # 200
print(Animal._Animal__COUNT)  # 100
print(c._Animal__COUNT)  # 101

# c.__getweight()  父类的私有方法不可以通过,实例访问
c._Animal__getweight()  #在实例字典中找到了_Animal__weight,值为5


实例属性的访问顺序

class C:
    def __init__(self):
        print('C init ~~~~~')


class A(C):
    def __init__(self):
        #super().__init__()
        print('A init ~~~~')


class B(A):
    def __init__(self):
        #super().__init__()
        print('B init ~~~~')
# 构造实例对象时,先找B中的__new__,再找
# A中的__new__, 都没找到,最后找的是object
# 中的__new__; 出厂配置时,初始化的顺序B,A,C
# 如果B中有__init__则只初始化B,
# 如果B中没有__init__, AC中都有,则只初始化A
# 如果B,A中都没有,只有C中有__init__,则只初始化C
# 如果A,B,C中都有__init__,想都初始化,则需要加2个super
b = B()

# 使用super()加载父类中的方法,然后在子类中增强

# 继承的正常使用方法1
class Animal:
    def __init__(self, age):
        self.age = age

    def getage(self):
        print(self.age)

class Cat(Animal):
    def __init__(self, age, weight):
        super().__init__(age)
        self.weight = weight

c = Cat(1, 10)
c.getage()  # 1

# 继承的正常使用方法2(getage函数的位置改变了)
class Animal:
    def __init__(self, age):
        self.age = age

class Cat(Animal):
    def __init__(self, age, weight):
        super().__init__(age)
        self.weight = weight

    def getage(self):
        print(self.age)

c = Cat(1, 10)
c.getage()  # 1

以下代码只是为了测试

class Animal:
    __COUNT = 100
    HEIGHT = 0

    def __init__(self, age, weight, height):
        self.__COUNT += 1
        self.age = age
        self.__weight = weight
        self.HEIGHT = height

    def eat(self):
        print('{}  eat'.format(self.__class__.__name__))

    def __getweight(self):
        print(self.__weight)

    @classmethod
    def showcount(cls):
        print(cls)
        print(cls.__dict__)
        print(cls.__COUNT)

    @classmethod
    def __showcount2(cls):
        print(cls.__COUNT)

    def showcount3(self):
        print(self.__COUNT)


class Cat(Animal):
    NAME = 'CAT'
    __COUNT = 200

#
c = Cat(3, 5, 15)
c.eat()

# print(c.__COUNT)  #私有属性不可以通过实例直接访问访问
# CAT类的字典中_Cat__COUNT,访问方式_Cat__COUNT
# Animal类的字典中_Animal__COUNT ,访问方式_Animal__COUNT
# 实例的字典中没有这个属性,实例的字典中有_Animal__COUNT,访问方式_Animal__COUNT
print(Cat._Cat__COUNT)  # 200
print(Animal._Animal__COUNT)  # 100
print(c._Animal__COUNT)  # 101

# c.__getweight()  父类的私有方法不可以通过,实例访问
c._Animal__getweight()  #在实例字典中找到了_Animal__weight,值为5

super用法

# 继承的测试例子(正常代码不要这么写):
# getage在子类中实现

class Animal:
    def __init__(self, age):
        self.age = age

class Cat(Animal):
    def __init__(self, age, weight):
        super().__init__(age)
        self.weight = weight

    def getage(self):
        print(self.age)


c = Cat(5, 10)
c.getage()  # 5

# getage在父类中实现
class Animal:
    def __init__(self, age):
        self.__age = age

    def getage(self):
        print(self.__age)

        
class Cat(Animal):
    def __init__(self, age, weight):
        super().__init__(age)  # 等价于super(Cat, self).__init__(age)
        # 执行完super,在c实例的字典中多了一个_Animal__age : 3
        self.__age = age + 1  # 执行完这一句,在c实例的字典中多了一个_Cat__age:4
        self.weight = weight

        
c = Cat(3, 10)
c.getage()  # Cat类中没有getage方法,所以到父类中找getage方法,找的是c实例字典中的_Animal__age方法,所以返回3


class Animal:
    def __init__(self, age):
        self.__age = age


class Cat(Animal):
    def __init__(self, age, weight):
        super().__init__(age)
        self.__age = age + 1
        self.weight = weight

    def getage(self):
        print(self.__age)

c = Cat(3, 10)
c.getage()  # 4

class A:
    def __init__(self, a, d=10):
        self.a = a
        self.__d = d

    def showd(self):
        return self.__d

class B(A):
    def __init__(self, b, c):
        self.b = b
        self.c = c
        A.__init__(self, b+c, b-c)
        #super(C, f).__init__(2)

    def printvalues(self):
        print(self.b)
        print(self.a)
        return self.showd()

b = B(2, 3)
print(b.printvalues()) # b=2, a=5 _A__d=-1

print(b.showd()) # -1

class Animal:
    def __init__(self, name):
        self._name = name

    @property
    def name(self):
        return self._name  #不可以用return self.name, 会形成递归

    def shout(self):
        print('{} shouts'.format(self.__class__.__name__))


class Cat(Animal):
    def __init__(self, name):
        super().__init__(name)
    def shout(self):  #override
        print(super())  # 返回2个值,打印的是super()实例自己,包括当前类和当前类对象,super类实例化时,也是接受的这两个参数 。
        # super().shout()  
        # 等价于super(self.__class__, self) 
        # 等价super(Cat, self)
        # 等价于super(Cat, self).shout()
        # 等价于Animal.shout(self) 不推荐
        # 等价于self.__class__.__base__.shout(self) #坚决不用,认识就可以
        super().shout()
        
        print('miao miao')


c = Cat('tutu')
c.shout()  # 'Cat shout'
print(c.name)  # tutu

属性装饰器

# 注意事项,递归
class People:
    def __init__(self, name):
        self.name = name
        
        #初始化时会先执行self.name = name,name现在为只读属性,在报递归错误之前(name(self)==self.name),会报cannot set attribute 错误, 因为只加一个@property,name会变成只读属性。

    @property
    def name(self):
        return self.name
      
p = People('name: tutu')
print(p.name)

因为发生了递归,错误例子

class Person:
    def __init__(self, name):
       self.name = name
        # 遇到self.name= name,  就会找到@name.setter,形成递归
            
    @property
    def name(self):
        return self.name

    @name.setter
    def name(self, name):
        self.name = name

p = Person('name: tutu')
print(p.name)   #会形成递归,因为加上property后,name变成self的属性,name(self)等价于self.name

可读可写属性正确的例子1:

class Person:
    def __init__(self, name):
       self.__name = name

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self, name):
        self.__name = name

# 实例字典 {'_Person__name': 'name: tutu'}
# 类字典 {'name': }
p = Person('name: tutu')
print(p.__dict__)
print(Person.__dict__)
print(p.name)

只读属性正确的例子2:

class Person:
    def __init__(self, name):
       self.__name = name

    @property
    def new_name(self):
        return self.__name
      
p = Person()
print(p.new_name)

可读可写属性例子3

class Person:
    def __init__(self):
        self.__eyecolor = 'blue'

    @property
    def eyecolor(self):
        return self.__eyecolor

    @eyecolor.setter
    def eyecolor(self, value):
        self.__eyecolor = value

    @eyecolor.deleter # 用的不多
    def eyecolor(self):
        del self.__eyecolor

p = Person()
print(p.__dict__['_Person__eyecolor'])

#p.__eyecolor #访问不到
print(p.eyecolor) #eyecolor函数可以,当作属性来用
p.eyecolor = 'yellow' #修改属性,如果这个属性是只读模式,则不用写下面的setter和deleter方法
del p.eyecolor

可读可写属性例子4

# @property, 适合只有一个属性,这种方法只支持隐式调用不支持显示调用
# setter方法中一定要加判断,不然属性装饰器就没有意义了
class Person:
    def __init__(self):
        print('1')
        self._first_name = "tutu"
        print('3')

    @property
    def first_name(self):
        return self._first_name

    @first_name.setter
    def first_name(self, first_name):
        print('2')
        if not isinstance(first_name, str):
            raise TypeError('Expected a string')
        self._first_name = first_name

# 只是实例化不会打印2,只会打印1, 3
p = Person()
p.first_name = 'guai'
print(p.first_name)

property 例子1:

# 使用函数property实现
class Person:
    def __init__(self):
        self.__eyecolor = 'blue'

    def geteyecolor(self): return self.__eyecolor

    def seteyecolor(self, value): self.__eyecolor = value

    def deleyecolor(self): del self.__eyecolor

    eyecolor = property(geteyecolor, seteyecolor, deleyecolor, "I'm the 'eyecolor' property.")

p = Person()
print(p.eyecolor)
p.eyecolor = "red"
print(p.eyecolor)
print(p.__dict__)
print(Person.__dict__)

print(p.geteyecolor()) #需要加括号
p.seteyecolor('yellow')
print(p.geteyecolor())

显示及隐式,显示调用就是使用property函数,隐式调用就是使用property装饰器

property 例子2:

# 下面的例子同时支持,显示调用和隐式调用
# 显式调用情景,python需要被集成到庞大的系统基础设施时,例如
# 分布式对象插入到一个大型的分布式系统中时,显示调用比隐式调用
# 更方便
class Person:
    def __init__(self, first_name):
        print('1')
        self._first_name = first_name
        print('3')

    def get_first_name(self):
        return self._first_name

    def set_first_name(self, first_name):
        print('2')
        if not isinstance(first_name, str):
            raise TypeError('Expected a string')
        self._first_name = first_name

    first_name = property(get_first_name, set_first_name)


p = Person('tom')
print(p.get_first_name())  # 'tom'

p.set_first_name('jerry')
print(p.get_first_name())  # 'jerry'

# 加了property函数后,也可以通过属性访问
print(p.first_name)  # 'jerry'
p.first_name = 'tom'
print(p.first_name)  # 'tom'

描述器

描述器在python中应用非常广泛,类如staticmethod()、classmethod()都实现了非数据描述器,property()函数实现为一个数据描述器。

Descriptors 定义 : 用到3个魔术方法的称为描述器

__get__、__set__、__delete__

实现描述器的2个条件

  • 2个类,属主类和描述器类,描述器类中有__get__, 或者其他2个魔术方法
  • 属主的类属性,设置为描述器的类实例

分类:

  • 非数据描述器(__get__)

    实例可以重新定义和覆盖方法,准许单个实例获取与同一类其他实例不同的行为

  • 数据描述器(__get__)(__set__)

添加非数据描述器和数据描述器实例属性的访问顺序:

  • 实例__dict__ —> 非数据描述器get方法

  • 数据描述器get方法 —> 实例_dict_ ->类字典

非数据描述器,找实例字典例子

class A:
    def __init__(self):
        print('A.init')
    def __get__(self, instance, owner):
        print('A.__get__')
        return self
class B:
    x = A()
    def __init__(self):
        print('B.init')
        self.x = 'b.x'

b = B()
# 实例的属性访问,不会调用A.__get__,会找实例自己的字典
# 在__init__中找了self.x,所以返回'b.x'
print(b.x)

非数据描述器,找非数据描述器get方法例子:

class A:
    def __init__(self):
        pass

    def __get__(self, instance, owner):
        return '9999'
class B:
    x = A()

    def __init__(self):
        pass
b = B()
print(b.x)  # 9999

数据描述器,先找get方法

class A:
    def __init__(self):
        pass

    def __get__(self, instance, owner):
        return self

    def __set__(self, instance, value):
        self.data = value
        instance.__dict__['x'] = '2334'


class B:
    x = A()

    def __init__(self):
        self.x = 'b.x'

b = B()

print('#########')
print(b.x)  # 返回<__main__.A object at 0x10ed2d0b8>
print('#########')


print(b.__dict__)  # {'x': '2343'}
print(B.__dict__)  # {'x': <__main__.A object at 0x10a305048>}
print(B.__dict__['x'].__dict__)  # {'data': 'b.x'}

数据描述器综合例子:

class A:
    def __init__(self):
        self.a1 = 'a1'
        print('A.init')

    def __get__(self, instance, owner):
        # print(self.__dict__)
        print('A.__get__')
        return self

    def __set__(self, instance, value):
        print('A.__set__')
        print('first print')
        self.data = value
        instance.__dict__['x'] = '2334'


class B:
    x = A()

    def __init__(self):
        print('B.init 1')
        self.x = 'b.x'
        print('B.init 2')


print('*********')
# 实例化时就会调用A.__set__
# 调用过程:先执行B类中的__init__方法,发现x属性是类描述器(在定义后执行时就存下来了),
# 所以执行装饰器中的__set__方法。打印顺序依次是print('B.init 1') -> print('A.__set__')
#         print('first print') -> print('B.init 2')
b = B()

print('*********')
print(A().__dict__)  # 打印A.init, {'a1': 'a1'}

print('#########')
# 实例的属性访问,会调用A.__get__,会先找描述器的字典,再找实例自己的字典,
# 然后找B类的字典,最后找到了。
# 数据描述器中,获得实例的属性
# print(a.__dict__)==print(b.x.__dict__)  # {'a1': 'a1', 'data': 'b.x'}
print(b.__dict__)  # {'x': '23'}
print(B.__dict__)  # 含有{'x': <__main__.A object at 0x10a305048>}
print(b.x)

print('***********')
# 打印a1
print(b.x.a1)  # 'a1'

print('##########')
# 打印b.x
print(b.x.data)  # b.x
print(b.x.__dict__)  # {'a1': 'a1', 'data': 'b.x'}

在外部增加属性赋值:

class A:
    def __init__(self):
        self.a1 = 'a1'
        print('A.init')

    def __get__(self, instance, owner):
        # print(self.__dict__)
        print('A.__get__')
        return self

    def __set__(self, instance, value):
        print('A.__set__')
        print('first print')
        self.data = value


class B:
    x = A()

    def __init__(self):
        print('B.init 1')
        self.x = 'b.x'
        print('B.init 2')


b = B()
b.x = 500
print(b.x)  # 会调用__set__方法, __main__.A object

B.x = 600
print(b.x)  # 600

__get__

  class A:
      def __init__(self):
          self.a1 = 'a1'
          print('A.init')
  
      def __get__(self, instance, owner):
          print('A.__get__')
  
  
  class B:
      x = A()
  
      def __init__(self):
          print('B.init')
  
  # 如果只是定义,虽然B中有x = A()
  # 但是,没有获取x属性,则不执行__get__方法
  
  
  print(B.x)  # 打印A.__get__,因为__get__方法返回None,
  # 所以打印None
  
  class A:
      def __init__(self):
          self.a1 = 'a1'
          print('A.init')
  
      def __get__(self, instance, owner):
          print('A.__get__')
          return self
  
  
  class B:
      x = A()
  
      def __init__(self):
          print('B.init')
  
  
  print(B.x.a1)  # __get__方法返回self,即A实例,
  # 这个A实例赋值给了x, 所以x有属性a1
  

属主中,只有通过类属性访问,才可以触发描述器的__get__,通过实例属性访问,则不可以

  
  class A:
      def __init__(self):
          self.a1 = 'a1'
          print('A.init')
  
      def __get__(self, instance, owner):
          print('A.__get__')
          return self
  
  
  class B:
      x = A()
  
      def __init__(self):
          print('B.init')
          self.b1 = A()
  
  
  print('**********')
  # B的实例没有x属性回去B类的字典中去找,所以触发
  # __get__方法
  b = B()
  print(b.x)
  print(b.x.a1)
  
  # b1是实例属性,不会触发 __get__方法
  print(b.b1)

实例不能覆盖属性的行为

  class A:
      @classmethod
      def foo(cls):  # 非数据描述器
          pass
  
      @staticmethod  # 非数据描述器
      def bar():
          pass
  
      @property  # 数据描述器
      def z(self):
          return 5
  
      def getfoo(self):  # 非数据描述器
          return self.foo
  
      def __init__(self):  # 非数据描述器
          self.foo = 100
          self.bar = 200
          # self.z = 300
  
  a = A()
  # foo bar 都可以在实例中覆盖,但是z不可以
  print(a.__dict__)
  print(A.__dict__)

3.6新增方法__set_name__(不多)

可以知道属主类

class A:
    def __init__(self):
        print('A init')

    def __get__(self, instance, owner):
        print(1, self, instance, owner)
        return self

    def __set_name__(self, owner, name):
        print(2, self, owner, name)
        self.name = name


class B:
    x = A()  # 类属性创建时调用描述器的__set_name__方法

# 不需要创建B实例,直接运行就会打印__set_name__方法内部内容
# name为x

# 调用__get__方法,打印1和A实例
print(B().x)

简单例子:实现静态方法和类方法

静态方法

class StaticMethod:
    def __init__(self, fn):
        self.fn = fn

    def __get__(self, instance, owner):
        return self.fn
class A:
    @StaticMethod
    def f1(x, y):  # 在解释器经过时,f1就被装饰过了,此时符f1指向StaticMethod类的实例
        print(x + y)
a = A()
a.f1(4, 5)  # a点f1会先调用类StaticMethod的__get__方法,然后调用函数f1

类方法:

from functools import partial
class ClassMethod:
    def __init__(self, fn):
        self.fn = fn

    def __get__(self, instance, owner):  # A的实例,与类A
        return partial(self.fn, owner)
class A:
    @ClassMethod
    def f1(cls, x, y):  # f1是ClassMethod实例
        print(x + y)
a = A()
a.f1(4, 5)  # a.f1 返回被固定了一个参数的fn
A.f1(4, 5)

参数检查(3种)

1、写函数 2、装饰器 3、描述器

# 1、写函数
class Person:
    def __init__(self, name: str, age: int):
        params = ((name, str), (age, int))
        if not self.checkdata(params):
            raise TypeError('params type error')
        self.name = name
        self.age = age

    def checkdata(self, params):
        for param, typ in params:
            if not isinstance(param, typ):
                return False
        return True

# p = Person('tom', '10')

2、装饰器

import inspect


def check_params(cls):
    def wrapper(*args, **kwargs):
        params = inspect.signature(Person).parameters
        values = list(params.values())

        return cls
    return wrapper



class Person:
    def __init__(self, name, age: int = 10, *args, weight=90, **kwargs):
        self.__name = name
        self.__age = age


# p = Person('tom', 10)

3、描述器

# 版本1
class TypeCheck:
    def __init__(self, attr, typ):
        self.attr = attr
        self.typ = typ

    def __get__(self, instance, owner):
        if instance:      # ????????????
            return instance.__dict__[self.attr]
        return self

    def __set__(self, instance, value):
        self.value = value
        if not isinstance(value, self.typ):
            raise TypeError('{} is requirement {}'.format(self.attr, self.typ))
        instance.__dict__[self.attr] = value


class Person:
    name = TypeCheck('name', str)  # 硬编码
    age = TypeCheck('age', int)  # 不优雅

    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age
        self.__dict__['weight'] = 90


p = Person('tom', 18)
print(p.__dict__)
# print(Person.__dict__)
print(p.name)
print(p.age)
print(p.weight)

# 版本2
# 需求:要求Person中只存在__init__()方法,注入name, age类属性
# 且使用描述器,提起__init__()方法的形参名称和注解类型


class TypeCheck:
    def __init__(self, attr, typ):
        self.attr = attr
        self.typ = typ

    def __get__(self, instance, owner):
        if instance:      # ????????????
            return instance.__dict__[self.attr]
        return self

    def __set__(self, instance, value):
        self.value = value
        if not isinstance(value, self.typ):
            raise TypeError('{} is requirement {}'.format(self.attr, self.typ))
        instance.__dict__[self.attr] = value


def propsinject(cls):
    params = inspect.signature(cls).parameters
    for name, param in params.items():
        if param.annotation is not inspect._empty:
            # 当参数有注解时,就将属性注入到类中
            setattr(cls, name, TypeCheck(name, param.annotation))
    return cls


@propsinject
class Person:
    # 类属性,由装饰器注入,不需要手动把属性都写出来
    # name = TypeCheck('name', str)
    # age = TypeCheck('age', int)

    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age
        self.__dict__['weight'] = 90


p = Person('tom', 18)
print(p.__dict__)
# print(Person.__dict__)
print(p.name)
print(p.age)
print(p.weight)

# 版本3
# 将版本2中的函数装饰器改装成类装饰器
class TypeCheck:
    def __init__(self, attr, typ):
        self.attr = attr
        self.typ = typ

    def __get__(self, instance, owner):
        if instance:      # ????????????
            return instance.__dict__[self.attr]
        return self

    def __set__(self, instance, value):
        self.value = value
        if not isinstance(value, self.typ):
            raise TypeError('{} is requirement {}'.format(self.attr, self.typ))
        instance.__dict__[self.attr] = value


class PropsInject:
    def __init__(self, cls):
        self.cls = cls
        params = inspect.signature(cls).parameters
        for name, param in params.items():
            if param.annotation != param.empty:
                # 当参数有注解时,就将属性注入到类中
                setattr(cls, name, TypeCheck(name, param.annotation))

    def __call__(self, *args, **kwargs):
        return self.cls(*args, **kwargs)  # 构建一个新的Person对象

@PropsInject
class Person:  # Person = PropsInject(Person)
    # 在类装饰器中Person只能是PropsInject的实例,但是类PropsInject
    # 中有__call__方法,所以Person('jerry', 10), 还可以调用
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age
        self.__dict__['weight'] = 90


p = Person('tom', 18)
print(p.__dict__)
# print(Person.__dict__)
print(p.name)
print(p.age)
print(p.weight)

产生递归的错误例子:

class TypeCheck:
    def __init__(self, name, typ):
        self.name = name
        self.type = typ

    def __get__(self, instance, owner):
        pass

    def __set__(self, instance, value):
        print('A.__set__')
        if not isinstance(value, self.type):
            raise TypeError
        setattr(instance, self.name, value)


class Person:
    name = TypeCheck('name', str)
    age = TypeCheck('age', int)

    def __init__(self, name:str, age:int):
        self.name = name
        self.age =age


p = Person('tom', 10)
# 会产生递归, 实例化时走到self.name = name, 然后调用
# __set__方法,执行setattr,相当于执行给p的实例增加,'name'
# 属性,属性值为name, 又回到self.name = name,所以递归

魔术方法5*

  • enter与exist方法

    当类中实现 __enter__和__exit__了方法,将 类的实例 放在with语法后面,则会执行__enter__ 方法,如果加了as,__enter__方法的返回值则是as后面的内容,然后执行with语句块,无论遇没遇到异常都会执行__exit__方法, __exit__方法的返回值默认是None,遇到异常就会抛出,如果将返回值改成等效为True的对象,则会压制异常。(具体例子参见上下文)
    
  • new与init方法

    当实例化一个类时会先调用__new__方法,然后调用__init__,如果类中没实现,会到父类中去找__new__和__init__
    class A:
      def __init__(self)
          pass
    a = A()
    
  • repr与str

    当直接打印实例时会执行类的__str__方法,如果这个方法没实现会找__repr__方法,当打印类的实例,并且这个实例放在列表中时,会执行__repr__方法, 如果只实现了__str__,则会直接打印实例对象。
    
  • setattr getattr setitem getitem等

    c = Cart()
    
    # __setattr__
    # 相当于调用实例的__setattr__方法
    c.name = 'tutu'
    print(c.name)
    
    # __getattr__
    # 相当于调用实例的__getattr__方法
    print(c.name)
    
    # __getitem__,key可以是索引或者键(可哈希)
    # 加[]相当与调用实例的__getitem__方法
    print(c[0])
    
    
    # __setitem__
    # 相当于调用实例的__setitem__方法
    c[0] = 'tutu'
    print(c)
    
  • len() 与__len__

    # 当类中实现了__len__方法时
    len(A()) # 返回__len__方法返回值
    len(A)  # 报错,好像魔术方法都是实例的
    
    # 当类中没实现__len__方法
    len(A()) # 报错
    len(A)  # 报错
    
  • iter

容器相关方法

# 将购物出改装成容器练习
# 也可以通过继承的方式将购物出改装成容器
class Item:
    def __init__(self, name, price):
        self.name = name
        self.price = price

    def __str__(self):
        return '名称:{},价格:{}'.format(self.name, self.price)

    __repr__ = __str__

class Cart:
    def __init__(self):
        self.items = []

    def additem(self, item):
        self.items.append(item)
        return self

    def __len__(self):
        return len(self.items)

    def __str__(self):
        return str(self.items)

    def __iter__(self):
        yield from self.items
        # 等价于,return iter(self.items)

    def __getitem__(self, index):
        return self.items[index]

    def __setitem__(self, key, value):
        self.items[key] = value

    __add__ = additem

    # __repr__ = __str__

c = Cart()
# 大小
print(len(c))
#__bool__
# 调用函数bool(), 返回布尔值,没有定义__bool__,就找__len__返回长度,非0为真
# 如果__len__也没有定义,那么所有实例都返回真值

class A: pass

a = A()
print(bool(a))  #True
print(bool(A))  #True

class A:
    def __len__(self):
        return 0

a = A()
print(bool(a))  # False
print(bool(A))  # True 只对实例起作用,对类A,不起作用,还是父类中的True

# add及可视化
c.additem(Item('car', 200)).additem(Item('earphone', 10))
print(c) # 如果类Cart中没有__str__,则返回一个object,如果类Item中没有__str__
# 则外层的object有包括几个object,类Cart中__str_显示最外层的列表,类Item中__str__
# 显示里层的名称和价格
c + Item('macbook', 300) + Item('phone', 400)
print(c)


# 遍历,返回一个新的迭代器对象
# 因为类中有__iter__方法,所以迭代实例c, 相当于迭代yield from形成的生成器对象
# 因为类Item中有__str__方法,所以可以看到具体的内容
for i in c:
    print(i)

print('=' * 10)


#in 成员运算符
#__contains__方法如果没有实现
# 就调用__iter__方法,判断目标字符串是否在,迭代器中
print('tutu' in c)

#__missing__字典或子类使用__getitem__(),如果key, 不存在,会调用此方法

构造器与析构器

class Person:
    def __init__(self, name): #构造器,构造函数,资源申请
        self.name = name

    def __del__(self): #析构器,构造函数,资源释放
        print('del instance', self)

def test():
    p = Person('tom')
    del p   #del x 并不直接调用 x.__del__() --- 前者会将 x 的引用计数减一,而后者仅会在 x 的引用计数变为零时被调用
    # 即在函数程序执行过程中引用计数变成0,调用了__del__
    print('*' * 10)
test()  # 执行完函数,不会打印:del instance <__main__.Person object at 0x10236c588>
# 因为在函数内部类对象的引用计数已经被清0了,会在号上面打印,起作用的是del

def test():
    p = Person('tom')
    print('*' * 5)
test()  # 执行完函数,才会执行__del__ 在*号之后打印:del instance <__main__.Person object at 0x10236c588>

# p = Person('tom')
# print(p.name) # 执行完最后一句print,才会执行__del__,打印:del instance <__main__.Person object at 0x10236c588>

#实例化 ?????????????????
#__new__是静态方法,所以第一参数必须手动给,这个方法很少用到
# 即使创建了该方法,也会调用一次,super().__new__(cls)
class A:
    def __new__(cls, *args, **kwargs):
        super().__new__(cls)

    def __init__(self, name, age):
        self.name = name
        self.age = age


a = A('tom', age=20)
print(a.name)

__call__方法

# 在类中定义一个__call__方法,**实例**就可以像函数一样调用

class Adder:
    def __call__(self, *args):
        self.result = sum(args)
        return self.result

# 以下两种调用方法等效

print(Adder()(*range(5)))

a = Adder()
print(a.__call__(*range(5)))

a = Adder()
print(callable(Adder))  #实例与类都是可调用的
print(callable(a))

# 函数调用的本质:

# 正常的函数调用,如bin() ,实际是调用了bin.__call__()

__hash__

# 判断一个对象是否可hash, isinstance(p1, collections.Hashable)
# list实例为什么不可以hash?
# 因为List类中有__hash__ = None,所以list实例不可以hash, 但是list类本身是可以hash的,print(hash(list)),
# 所有的类都继承自object, object类中有__hash__(),所以所有的类都是可hash的,
# 如果所有的类不可hash,需要在object中设置

class A:
    pass

print(A)
print(A())  #什么都不写,类和实例也都可hash
print('=' * 10)

class A:
    __hash__ = None
print(hash(A)) #可hash,
#print(hash(A())) #不可hash,因为None类型不可以这么使用None(A())
# print(A.__hash__())  # 不等价于 hash(A), 因为前面不可调用,但是后面
# 可hash


#集合去重的本质,需要hash值相同,并且内容相同
class A:
    def __init__(self, name):
        self.name = name

    def __hash__(self):
        return hash(self.name)

    def __eq__(self, other):
        return self.name == other.name

    def __str__(self):
        return str(self.name)

    __repr__ = __str__


a = A('tom')
b = A('tom')
print(a is b)  #False
print(a == b)  #True
print(hash(a)) #5241461326091852353
print(hash(b)) #同上
print({a, b})  #去重了只剩下{tom},因为a b值相等,集合中的元素是repr的返回值


a = A('tom')
b = A('jerry')
print(a is b)  #False 判断的是id(a) id(b)
print(a == b)  #False
print(hash(a)) #5241461326091852353
print(hash(b)) #不同上
print({a, b})  #{tom, jerry} 没有去重,因为a b值不相等,集合中的元素是repr的返回值

# 将hash魔术方法的返回值修改了
class A:
    def __init__(self, name):
        self.name = name

    def __hash__(self):
        return 123

    def __eq__(self, other):
        return self.name == other.name

    def __str__(self):
        return str(self.name)

    __repr__ = __str__


a = A('tom')
b = A('tom')
print(a is b)  #False
print(a == b)  #True
print(hash(a)) #123
print(hash(b)) #同上
print({a, b})  #去重了只剩下,tom

a = A('tom')
b = A('jerry')
print(a is b)  #False
print(a == b)  #False
print(hash(a)) #123
print(hash(b)) #同上
print({a, b})  #{tom, jerry} 没有去重

#去掉魔术方法eq
class A:
    def __init__(self, name):
        self.name = name

    def __hash__(self):
        return 123

    def __str__(self):
        return str(self.name)

    __repr__ = __str__


a = A('tom')
b = A('tom')
print(a is b)  #False
print(a == b)  #False  如果类中没写__eq__那么这一行相当于判断is,地址是否相同
print(hash(a)) #123
print(hash(b)) #同上
print({a, b})  #{tom, tom},没有去重复,因为a b 值不想等
print(hash(1)) #居然等于1

可视化方法

class Person:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return ''.format(self.name)

    def __bytes__(self):
        return str(self).encode()

    __repr__ = __str__
p = Person('tom')
print(p)
print(str(p))
print(repr(p))
print('{}'.format(p))
print(bytes(p))  # b''

运算符重载

# 当类中需要进行大量运算时使用,int类几乎实现了所有操作符,可以作为参考
# 自己是现实只需要实现3中运算符,其他3种可以推断出来
# __eq__ 由等于可以推出不等于
# __ge__ 由大于等于可以推断出小于等于
# __gt__ 由大于可以推断出小于
class A:
    def __init__(self, name, age=18):
        self.name = name
        self.age = age

    def __eq__(self, other):  #等效于 ==
        return self.age == other.age

    def __gt__(self, other):
        return self.age > other.age

    def __ge__(self, other):
        return self.age >= other.age

    def __add__(self, other):
        return self.age + other.age

    def __iadd__(self, other):
        self.age += other.age
        return self.age

    def __sub__(self, other):
        pass

    def __isub__(self, other):
        pass

    def __str__(self):
        return self.name

    __repr__ = __str__

a = A('tom')
b = A('jerry', 25)
# 可以直接对两个实例进行排序
# 因为实例中含有__str__方法,所以会显示名字[tom, jerry],默认是升序,实际比较
# 的是实例,名字只是显示
print(sorted([a, b]))

print(a == b)
print(a > b)
print(a <= b)
print(a != b)

# 在类上面加一个装饰器@total_ordering,但是这个装饰器效率不高,一般不用
# from functools import total_ordering

print('=' * 10)
print(a + b)
a += b
print(a)

# 另一个例子
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __add__(self, other):
        return self.x + other.x, self.y + other.y

    # #方法1
    # def __iadd__(self, other):
    #     self.x, self.y = self + other
    #     return self

    # #方法2
    # def __iadd__(self, other):
    #     return Point(*(self + other))

    # #方法3
    # def __iadd__(self, other):
    #     self.x += other.x
    #     self.y += other.y
    #     return self

    #方法4
    def __iadd__(self, other):
        self.x = self.x + other.x
        self.y = self.y + other.y
        return self


    def __str__(self):
        return str((self.x, self.y))

p1 = Point(1, 2)
p2 = Point(2, 1)
p1 += p2  #为什么一定要有返回值呢???????????????
print(p1)

查看属性和方法__dir__

# 特殊属性
# __name__  类、函数、方法等的名字,实例没有
# __module__ 类定义所在的模块名字
# __class__ 实例或者类所属的类
# __bases__ 类的基类,返回一个元组,顺序为它们在基类列表中出现的顺序
# __doc__ 类或者函数的文档字符串,如果没有定义则为None
# __mro__ 类的继承顺序,class.mro()返回的结果保存在,__mro__中
# __dict__ 类或者实例的属性

#查看属性的方法__dir__
#dir()函数操作实例就是调用__dir__()
# dir(obj)
# obj的类型:
# 模块名,返回的**列表**包含模块的属性名和变量名
# 例如:print(dir('OOP魔术方法练习.py'))
# 类或者类型,返回的列表包含类的属性名,及它祖先的属性名
# 类的实例,
# 有__dir__方法,返回可迭代对象的返回值
# 没有__dir__方法,返回列表包含,实例的属性名,类的属性名,祖先类的属性名
# dir() #里面什么都不写
# 在模块中,返回模块的属性和变量名
# 在方法或者函数中,返回本地作用域的变量名

# 内建函数:
print(locals()) # 返回当前作用域中,变量**字典**
print(globals()) # 返回当前模块全局变量的字典

方法重载(overload)
函数名字相同,但是参数的类型不同, 其他语言可以使用,但是python的重载,如下面的例子所示, 同一个add函数可以实现,整数相加,字符串相加,也可以接受多个参数, 即灵活的形参,python语法本身就实现了其他语言的重载

class Add:
    def add(self, a, b):
        return a + b
Add().add(1, 2)
Add().add('a', 'b')

override 重写,覆盖

猴子补丁(发现python的方法或类写的不好,用的不多)

from test2 import Person
from test3 import get_score
def monkeypatch4Person():
    Person.get_score = get_score
monkeypatch4Person()
# 修改了Person,中的get_score方法

上下文管理

with open('test') as f:
    pass
 # open('test')调用open对象的__enter__方法,并且这个函数的返回值
# 赋值给f
# with语句块内容执行完成后,又调用open对象的__exit__()方法
  • 上下文管理对象

一个类实现了__enter__() _exit_(),就属于上下文管理对象

__enter__()需要有返回值

# l
f = open('test.py', encoding='utf8')
with f as p:
    print(f is p) #True
  print(f == p) #False
  
class Point:
  def __init__(self):
    pass
  
  def __enter__(self):
    return self #添加返回值
  
  def __exit__(self, exc_type, exc_val, exc_tb):
    pass
 
# 2
p = Point()
with p as f: #等价 f = p.__enter__()
  print(p is f) #True
  print(p == f) #True
#因为__enter__有返回值
       

__exit__(self, exc_type, exc_val, exc_tb )

方法参数:

没有异常是都为None 

有异常时

     exc_type:异常类型

    exc_val:异常值

    exc_tb:异常追踪信息   

_\_exit\_\_()方法默认返回None,等效False,   异常会正常抛出

如果返回值是等效于True的值,则压制异常值。具体例子见文档魔术方法上下文管理
例子1
#  __exit__返回None,不会压制异常,会执行函数__exit__(),打印1
# 不会打印2
class Point:
    def __init__(self):
        pass

    def __enter__(self):
        return self  # 添加返回值

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('1')


p = Point()
with p as f:
    raise Exception('Error')
    print('2')


例子2
# __exit__()等效返回True, 异常会被压制,
# 与上面相同不会打印2
class Point:
    def __init__(self):
        pass

    def __enter__(self):
        return self  # 添加返回值

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('1')
        return 123


p = Point()
with p as f:
    raise Exception('Error')
    print('2')

  • 上下文管理的安全性

    在with语句中如果抛出异常,或者调用sys.exit(1),上下文管理中的

    __enter__() _exit_() 方法也都会执行,文件描述符会被关闭,很安全

    具体例子见02魔术方法上下文管理

  • 上下文管理例子

    为函数增加计时功能

    方法1,装饰器

    from functools import wraps
    import time
    import datetime
    def timeit(fn):
      @wraps(fn)
      def wrapper(*args, **kwargs):
          start = datetime.datetime.now()
          ret = fn(*args, **kwargs)
          delta = (datetime.datetime.now() - start).total_seconds()
          print(delta)
            return ret
      return wrapper
    @timeit
    def add(x, y):
      time.sleep(2)
      return x + y
    

方法2,上下文(需要写一个类)

import time
import datetime
class TimeIt:
  def __init__(self, fn):
    self.fn = fn
  def __enter__(self):
    self.start = datetime.datetime.now()

  def __exit__(self, exc_type, exc_val, exc_tb):
      print(self.fn(1, 2))
      print((datetime.datetime.now() - self.start).total_seconds())

def add(x, y):
  time.sleep(2)
  return x + y

with TimeIt(add):
  pass
#fn函数的调用方式也可放在with语句中

#另一种方法,通过可调用对象实现(口诀with后面需要返回可调用对象)
class TimeIt:
  def __init__(self, fn):
    self.fn = fn
  def __enter__(self):
    self.start = datetime.datetime.now()
    return self
  def __exit__(self, exc_type, exc_val, exc_tb):
      print((datetime.datetime.now() - self.start).total_seconds())

  def __call__(self, x, y):
      return self.fn(x, y)

def add(x, y):
  time.sleep(2)
  return x + y

with TimeIt(add) as add: #当__enter__返回实例时,add指向实例,实例再调用call方法
  print(add(1, 2)) # add.__call__()

方法3,通过类装饰器实现(只要用到装饰器,就要考虑更新wrapper

from functools import update_wrapper, wraps
class TimeIt:
def __init__(self, fn):

    self.fn = fn

      # 更新文档,名字,字典等,方法1
      #wraps(fn)(self)

      #方法2
      #update_wrapper(self, fn)

      #方法3, WRAPPER_ASSIGNMENTS中才有__name__,这个方法只能更新__dict__
      #self.__dict__.update(fn.__dict__)

      #方法4, 只能更新指定的几个不能更新全部
      self.__name__ = fn.__name__
      self.__dict__.update(fn.__dict__)
      self.__doc__ = fn.__doc__

  def __call__(self, x, y):
      start = datetime.datetime.now()
      ret = self.fn(x, y)
      print(self.fn.__name__, (datetime.datetime.now() - start).total_seconds())
      return ret

@TimeIt  # add = TimeIt(add)
def add(x, y):
  time.sleep(2)
  return x + y

print(add(1, 2))


# 实例没有名字,但是函数和类都有名字
print(add.__name__)  #add
print(TimeIt.__name__)  #TimeIt

上下文应用场景

  1. 增强功能

    在代码执行前后增加代码,以增强其功能,类似装饰器

  2. 资源管理

    打开了资源需要关闭,例如文件对象、网络连接、数据库连接

  3. 权限验证

    在执行代码前,做权限的验证,在__enter__中处理

  • 实现简单上下文管理的装饰器

    context lib.contextmanager, 是一个装饰器实现上下文管理,但是没有with语句,也不用实现类, 和类中的_enter__() __exit_()等方法,而是通过关键yield实现,它装饰的是一个函数,这个函数需要是生成器,而且只能yield一个值。

    # 不添加异常的例子
    import contextlib
    
    @contextlib.contextmanager
    def foo():
        print('enter')
        try:
            yield [1, 2]  #yield后面只能有一个值,相当于__enter__方法的返回值
        finally:
            print('exit')
    
    with foo() as f:
        print(f)
    
    #执行过程:with语句执行到yield,有一个返回值付给f,然后执行with语句内部的内容
    #最后执行foo函数中的finally语句块的内容
    
    #执行结果:打印enter,with语句中f的返回值[1, 2]
    # exit,最后finally中的打印值,相当于__exit__中执行的内容
    
    
    # 添加异常的例子
    @contextlib.contextmanager
    def foo():
        print('enter')
        try:
            yield [1, 2]
        finally:
            print('exit')
    
    with foo() as f:
        raise Exception()
        print(f)
    # 添加异常
    # 会打印'enter','exit',但是with语句块的内容不会被执行,即不会打印[1, 2]
    

反射5*

是指程序运行时可以动态的获得属性和方法,python中具有反射能力的函数type(), getattr(),setattr()

定义:是指程序运行时,能够通过对象找出type, attribute,(class,),或者动态添加方法的能力。(运行时是指,解释性语言的程序被加载到内存中执行时,不同于编译性语言。python中具有反射能力的函数type(), getattr(),setattr()(instance(), callable(), dir(), hasattr())等)

  • 反射与装饰器,Mixin方式的差异:

      这种动态增加属性的方式,是运行时改变类或者实例的方式,但是装饰器和Mixin都是定义时就决定了,因此反射的具有更大的灵活性。
    
  • 内建函数hasattr(), setattr(), getattr()

    • 第2个位置的参数,必须是字符串

    • getattr() setattr() 等价于取点的方式获得和设置属性

    • getattr() setattr() 与魔术方法__getattr__ __setattr__关系

      • setattr()函数,当类中实现了__setattr__方法时,会走类中的这个方法

      • getattr()函数,当类中实现了__getattr__方法时,并且实例的字典中没有要获取的属性时,才会走类中的这个方法

      • 反射与内键函数的所有例子只需要看一个例子

        # 其实不用实现这两个魔术方法,就可以满足开发需求
        class Point:
            def __init__(self, x, y):
                self.x = x  # 这时会调用__setattr__
                self.y = y
        p = Point(1, 2)
        print(p.x)  # 1
        print(p.y)  # 2
        p.z = 9
        print(p.z)  # 9
        
        
  • getattr() # 等价于p.z
    setattr() # 等价于p.z = 10
    

你可能感兴趣的:(python面向对象)