Python 高级之 ------面向对象

Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的。

主要是以下的知识点

1. 创建类

用 class 语句来创建一个新类,class 之后为类的名称并以冒号结尾

    class ClassName:
       '类的帮助信息'   #类文档字符串
       class_suite  #类体

* 类的帮助信息可以通过ClassName.doc查看。
* class_suite 由类成员,方法,数据属性组成。
* 类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self。
* self代表类的实例(对象),代表当前对象的地址而非类 self 代表类的实例,self 在定义类的方法时是必须有的,虽然在调用时不必传入相应的参数
* self.class 则指向类。self 不是 python 关键字,我们把他换成 xxx任意的 也是可以的。
* 类继承 继承语法 class 派生类名(基类名)://… 基类名写在括号里,基本类是在类定义的时候,在元组之中指明的。
* 在继承中基类的构造(init()方法)不会被自动调用,它需要在其派生类的构造中亲自专门调用。
* 在调用基类的方法时,需要加上基类的类名前缀,且需要带上self参数变量。区别在于类中调用普通函数时并不需要带上self参数
* Python总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。(先在本类中查找调用的方法,找不到才去基类中找)
* 如果在继承元组中列了一个以上的类,那么它就被称作”多重继承” 。
* issubclass() - 布尔函数判断一个类是另一个类的子类或者子孙类,语法:issubclass(sub,sup)
* isinstance(obj, Class) 布尔函数如果obj是Class类的实例对象或者是一个Class子类的实例对象则返回true。
语法:
* 方法重写 同java
* 方法重载 函数中的参数个数 重载 运算符重载 (在类的对象中 重载对应的方法
这里写图片描述

2. 创建类的实例 (对象)

  • 在 Python 中并没有new这个关键字,类的实例化类似函数调用方式。以下使用类的名称 Employee 来实例化,并通过 init 方法接受参数

        "创建 Employee 类的第一个对象"
        emp1 = Employee("Zara", 2000)
        "创建 Employee 类的第二个对象"
        emp2 = Employee("Manni", 5000)
    
  • 类里面的方法 用户自己写的全是公有的非静态的只能用类的对象来访问 直接 对象.方法即可

    • 公有静态方法 由于所有方法都包含self 所以公用静态方法 必须传入你new的对象 所以可以用对象.方法来代替 所以这个方法虽然是静态的其实 是面向某个实例的 emp1 = Employee(“Manni”, 5000) Employee.displayEmployee(emp1) emp1.displayEmployee() 后面的两句是等效的
    • 私有方法 两个下划线开头,声明该方法为私有方法,不能在类地外部调用。在类的内部调用 self.__private_methods
  • 类里面的变量

    • 公有的非静态成员变量 一般都是在init()方法中定义的。slef.aa=1 类的成员变量必须赋值 不像其他语言有默认缺省值
    • 私有的非静态成员变量 两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。使用的时候同上self.__private_attrs。
    • 公有的静态成员变量 在类中直接命名 的值将在这个类的所有实例之间共享。

      public void aa() == def aa(slef)
      private void bb() == def __bb(slef)
      public static int cc == cc=0
      public int dd == __int__()方法里面的 slef.dd=0
      private int ee == __ee=0
      
  • 受保护类型的是以_单下划线开头的 同私有变量 保护类型的就是只在自己类中和子类中可以用
  • 你可以添加,删除,修改类的属性,即便类已经定义好了。直接对象.成员变量 尽量不要这样去做 这样很难去扩展了。

        emp1.age = 7  # 添加一个 'age' 属性
        emp1.age = 8  # 修改 'age' 属性
        del emp1.age  # 删除 'age' 属性
    
  • 也可以使用以下函数的方式来访问属性:

        hasattr(emp1, 'age')    # 如果存在 'age' 属性返回 True。
        getattr(emp1, 'age')    # 返回 'age' 属性的值
        setattr(emp1, 'age', 8) # 添加属性 'age' 值为 8
        delattr(emp1, 'age')    # 删除属性 'age'
    
  • Python内置类属性(静态成员方法)
    • dict : 类的属性(包含一个字典,由类的数据属性组成)
    • doc :类的文档字符串
    • name: 类名
    • module: 类定义所在的模块(类的全名是’main.className’,如果类位于一个导入模块mymod中,className.module 等于 mymod)
    • bases : 类的所有父类构成元素(包含了一个由所有父类组成的元组)
    • *

3. python对象实例销毁(垃圾回收机制)

  • Python 使用了引用计数这一简单技术来跟踪和回收垃圾。在 Python 内部记录着所有使用中的对象各有多少引用。
  • 在引用计数的基础上,还可以通过“标记-清除”(mark and sweep)解决容器对象可能产生的循环引用的问题。
  • 通过“分代回收”(generation collection)以空间换取时间来进一步提高垃圾回收的效率。
  • 当对象被创建时, 就创建了一个引用计数, 当这个对象不再需要时, 也就是说, 这个对象的引用计数变为0 时, 它被垃圾回收。但是回收不是”立即”的, 由解释器在适当的时机,将垃圾对象占用的内存空间回收。

3.1 引用计数

  1. 在Python中,大多数对象的生命周期都是通过对象的引用计数来管理的。
  2. 原理:当一个对象的引用被创建或者复制时,对象的引用计数加1;当一个对象的引用被销毁时,对象的引用计数减1;当对象的引用计数减少为0时,就意味着对象已经没有被任何人使用了,可以将其所占用的内存释放了。
  3. 虽然引用计数必须在每次分配和释放内存的时候加入管理引用计数的动作,然而与其他主流的垃圾收集技术相比,引用计数有一个最大的有点,即“实时性”,任何内存,一旦没有指向它的引用,就会立即被回收。而其他的垃圾收集计数必须在某种特殊条件下(比如内存分配失败)才能进行无效内存的回收。
  4. 引用计数机制执行效率问题:引用计数机制所带来的维护引用计数的额外操作与Python运行中所进行的内存分配和释放,引用赋值的次数是成正比的。而这点相比其他主流的垃圾回收机制,比如“标记-清除”,“停止-复制”,是一个弱点,因为这些技术所带来的额外操作基本上只是与待回收的内存数量有关。
  5. 如果说执行效率还仅仅是引用计数机制的一个软肋的话,那么很不幸,引用计数机制还存在着一个致命的弱点,正是由于这个弱点,使得侠义的垃圾收集从来没有将引用计数包含在内,能引发出这个致命的弱点就是循环引用(也称交叉引用)。循环引用可以使一组对象的引用计数不为0,然而这些对象实际上并没有被任何外部对象所引用,它们之间只是相互引用。这意味着不会再有人使用这组对象,应该回收这组对象所占用的内存空间,然后由于相互引用的存在,每一个对象的引用计数都不为0,因此这些对象所占用的内存永远不会被释放。

3.2 标记-清除

  1. “标记-清除”是为了解决循环引用的问题。可以包含其他对象引用的容器对象(比如:list,set,dict,class,instance)都可能产生循环引用。
  2. 原理:“标记-清除”采用了更好的做法,我们并不改动真实的引用计数,而是将集合中对象的引用计数复制一份副本,改动该对象引用的副本。对于副本做任何的改动,都不会影响到对象生命周期的维护。
  3. 这个计数副本的唯一作用是寻找root object集合(该集合中的对象是不能被回收的)。当成功寻找到root object集合之后,首先将现在的内存链表一分为二,一条链表中维护root object集合,成为root链表,而另外一条链表中维护剩下的对象,成为unreachable链表。之所以要剖成两个链表,是基于这样的一种考虑:现在的unreachable可能存在被root链表中的对象,直接或间接引用的对象,这些对象是不能被回收的,一旦在标记的过程中,发现这样的对象,就将其从unreachable链表中移到root链表中;当完成标记后,unreachable链表中剩下的所有对象就是名副其实的垃圾对象了,接下来的垃圾回收只需限制在unreachable链表中即可。

3.3 分代回收

  1. 背景:分代的垃圾收集技术是在上个世纪80年代初发展起来的一种垃圾收集机制,一系列的研究表明:无论使用何种语言开发,无论开发的是何种类型,何种规模的程序,都存在这样一点相同之处。即:一定比例的内存块的生存周期都比较短,通常是几百万条机器指令的时间,而剩下的内存块,起生存周期比较长,甚至会从程序开始一直持续到程序结束。
  2. 从前面“标记-清除”这样的垃圾收集机制来看,这种垃圾收集机制所带来的额外操作实际上与系统中总的内存块的数量是相关的,当需要回收的内存块越多时,垃圾检测带来的额外操作就越多,而垃圾回收带来的额外操作就越少;反之,当需回收的内存块越少时,垃圾检测就将比垃圾回收带来更少的额外操作。为了提高垃圾收集的效率,采用“空间换时间的策略”。
  3. 原理:将系统中的所有内存块根据其存活时间划分为不同的集合,每一个集合就成为一个“代”,垃圾收集的频率随着“代”的存活时间的增大而减小。也就是说,活得越长的对象,就越不可能是垃圾,就应该减少对它的垃圾收集频率。那么如何来衡量这个存活时间:通常是利用几次垃圾收集动作来衡量,如果一个对象经过的垃圾收集次数越多,可以得出:该对象存活时间就越长。

猜你喜欢

转载自blog.csdn.net/shentanweilan9/article/details/79401812
今日推荐