美文网首页
Python学习笔记6

Python学习笔记6

作者: MetaT1an | 来源:发表于2018-09-02 11:06 被阅读0次

面向对象(补充)上一部分

  • 描述器
  • 生命周期
  • 内存管理
  • 面向对象三大特性
  • 类的设计原则

描述器

  • 描述器是一个对象,用来描述其他对象属性的操作;作用是对属性的操作做验证和过滤。
  • 前面只读属性案例中就是用到了描述器。
  • 在对象的内部增加一个描述器,可以接管对象属性的增删改查操作。
class Age:
    def __get__(self, instance, owner):     # instance是拥有 age属性的对象
        pass
    
    def __set__(self, instance, value):
        instance.v = value      # 将变量的值绑定在 Person的实例中
    
    def __delete__(self, instance):
        pass
    
class Person:
    age = Age()
    

# age实例是 p1和 p2两个对象所共享的,所以 Age对象及实例不应该具有属性,只单纯地提供方法即可
p1 = Person()
p1.age = 19      # 调用 __set__()
print(p1.age)    # 调用 __get__()

p2 = Person()
p2.age = 20
print(p2.age)   # 20
  • 资料描述器非资料描述器
    • 资料描述器:实现了__get__()__set__()
    • 非资料描述器:仅仅实现了__get__()
  • 实例属性和描述器重名时,操作的优先级如下:
    • 资料描述器 > 实例属性 > 非资料描述器

生命周期

  • 表示一个对象从创建到释放的过程
class Person:
    __count = 0
    
    def __init__(self):
        Person.__count += 1
        
    def __del__(self):
        Person.__count -= 1
        
    @classmethod
    def log(cls):
        print("we have %d people" % cls.__count)
        
p1 = Person()
Person.log()    # we have 1 people
p2 = Person() 
Person.log()    # we have 2 people

内存管理机制

  • 万物皆对象,不存在基本数据类型
  • 对于相等的整数 [-5, 正无穷) 和短小的字符串,Python会进行缓存,不会创建多个对象
n1 = 1
n2 = 1
print(id(n1), id(n2))   # 1708655056 1708655056
  • 容器对象:存储的对象,仅仅是其他对象的引用(列表)
  • 内存回收
    • 引用计数
      • 一个对象会记录着自身被引用的个数
      • 每增加一个引用,引用数+1,减少一个引用,引用数-1
      • 引用数为0的时候,会被当做垃圾进行回收
      • 会出现连个对象循环引用的问题
    • 垃圾回收
      • 从经历过引用计数机制但仍然未被释放的对象中,进行内存释放
      • 新增的对象个数 - 消亡对象的个数达到一定阈值时才进行垃圾检测
    • 分代回收
      • 分代回收是垃圾回收的高效解决方案,不需频繁地进行垃圾检测
      • 存活时间越久的对象,越不可能在后面的过程中变成垃圾
      • 设立0, 1, 2三代对象集合,对其中的对象进行不同频率的检测
      • 第一次检测存活下来的,从0代纳入1代,0代检测一定次数后开始检测1代,以此类推
  • 深拷贝和浅拷贝
    • 浅拷贝
    a = [1, 2, 3]
    b = a
    print(id(a), id(b))   # 2229855665608 2229855665608
    
    • 深拷贝
    import copy
    
    a = [1, 2, 3]
    c = copy.deepcopy(a)
    print(id(a), id(c))   # 2229855665608 2229861709896
    
    • copydeepcopy的区别
    import copy
    
    a = [1, 2, 3]
    b = [4, 5, 6]
    c = [a, b]
    d = copy.deepcopy(c)
    e = copy.copy(c)
    
    image
    • 使用copy拷贝可变类型时,进行单层次的深拷贝,若拷贝的是不可变类型,则进行浅拷贝。

面向对象三大特性

  • 封装继承多态
继承
  • 非私有的属性和方法可以被继承,继承不是拷贝了资源,而是具有了资源的访问权,资源的存储位置在父类中,实现资源重用
  • Python中可以使用多继承
# 所有的类都继承了 object类
# 所有的类对象都由 type实例化出来
class A:
    pass
    
class B:
    pass
    
class C(A, B):      # C 类继承了 A和 B类
    pass
    
print(C.__bases__)      # (<class '__main__.A'>, <class '__main__.B>)
print(int.__base__)     # (<class 'object'>)
print(bool.__base__)    # (<class 'int'>)
  • 几种继承的形式


    image
  • 资源查找顺序

    • 单继承链:C-->B-->A
    • 无重叠多继承链:按照单继承链深度优先查找(C-->B-->A-->D-->E)
    • 有重叠多继承链:广度优先查找(C-->B-->D-->A)
  • 资源覆盖

    • 在优先级较高的类中重新定义了同名的属性和方法,再次调用时,会调用到优先级较高类中的资源,并不是相关的资源在内存上被覆盖了,而是调用优先级出现了变化
  • selfcls

# 谁调用方法,self 和 cls 就是谁
# 带着参数去找方法

class A:
    def show(self):
        print(self)
        
    @classmethod
    def tell(cls):
        print(cls)
        
class B(A):
    pass

B.tell()        # <class '__main__.B'>
B().show()      # <__main__.B object at 0x027674D0>
  • 资源的累加
class A:
    def __init__(self):
        self.x = 2

class B(A):
    pass

class C(A):
    def __init__(self):
        self.y = 1

class D(A):
    def __init__(self):
        self.y = 1
        
class E(A):
    def __init__(self):
        super().__init__()      # 会调用 A的构造函数,参数可以省略
        # A.__init__(self)       //和上面等价,要传参
        self.y = 1
        
b = B()
print(b.x)      # 2, 调用了父类的构造函数,b 调用,x就是 b的

c = C()
print(c.y)      # 1, C有了构造函数,就调用 C的,A的构造函数不会被调用
print(c.x)      # 报错,没有这个属性

e = E()
print(e.x, e.y)     # 2, 1 
多态
  • Python是动态类型的语言,不需要严格意义上的多态
def test(obj):
    obj.func()
    
# 只要传入的参数有 func()这个方法,就可以传入进行执行,不用进行类型检测。
# 不需要按照其他静态语言那样沿着继承链进行方法调用形成多态

类的设计原则

  • 单一职责原则:一个雷只负责一项职责
  • 开放封闭原则:对外扩展开放,对内修改关闭
  • 里式替换原则:子类所继承下来的属性和方法都需能够合理地使用
  • 接口分离原则:功能一致的方法应该重新组成新的接口/类,进行细分
  • 依赖倒置原则:高层模块不应该直接依赖低层模块,核心是面向接口编程

相关文章

网友评论

      本文标题:Python学习笔记6

      本文链接:https://www.haomeiwen.com/subject/dnzpwftx.html