目录

构造器和解构器

构造器 __init__()

类函数 __init__() 是 Python 类中预定义的方法,需要被重载才会生效。以双下划线 “__” 开头和结尾, 在 Python 中使用这种命名方式的方法会被理解为是一种特殊方法, Python 的特殊方法功能非常丰富, 种类也很多, 在声明变量名的时候要注意不要和特殊方法重名.

通常,构造器用于在 实例化对象被创建后,返回这个实例之前 的这段时间里,执行一些特定的任务或设置。例如:初始化实例对象属性(以 self 关键字调用的属性)。它在实例化一个新的对象时被自动调用,所以除了初始化实例属性之外,还常被用于运行一些初步的诊断代码。其调用的具体步骤:

1. 创建类的实例化对象

2. Python 解析器检查类是否实现了构造器

3. 若有,则执行构造器的实现,且要求创建对象的时候传入对应的实参。实例对象会传递给第一个形参 self 。

4. 若没有,则直接返回实例对象

一般建议在构造器中设定需要初始化的实例属性,而且构造器应该返回 None,即没有 return 语句。

真·构造器 __new__()

__init__() 相比 __new__() 才是真正的构造器,实际上,在 Python 解析器中是先调用了 __new__() 生成一个实例,再将该实例对象传入 __init__() 实现初始化操作。但 __new__() 很少需要我们去重载,一般只有在派生了不可变类型的子类后需要重载,EG. 派生 String/Int/Tuple 等

为什么说 __new__() 是真·构造器呢?

因为这个特殊的类方法是真的返回了一个类的实例对象,而不像 __init__() 是传入了一个实例化对象。

EXAMPLE:不可表类型的派生

class RoundFloat(float):
def __new__(cls, val):
return float.__new__(cls, round(val, 2)) #因为 __new__ 是一个类方法,所以我们要显式的传递一个类对象

类 RoundFloat 是类 float 的子类,我们通过重载父类的 __new__() 构造器来定制一个新的不可变类型(Python 2.2之后将类和类型统一了,所以可以继承 Python 的内置数据类型)。当实例化 RoundFloat 的对象时,实际上是实例化了Python 内置数据类型 Float 的对象,并对这个对象做了一些定制化的操作(round(val, 2))。

NOTE:即便我们也可以通过重载 __init__() 来实现这个结果,但这里却不能这么做。因为如果 __new__() 没有被重载的话,仍会默认调用父类 Float 的构造器,创建 Float 类型的对象,而不是创建现在的 RoundFloat 类型对象。这也是两者的本质区别。

解构器 __del__()

解构器 __del__() 会在实例对象被垃圾回收之前被 Python 解析器调用一次(只会调用一次,之后就回收实例对象)。解构器是实例对象释放资源前提供特殊处理功能的方法。一般我们很少会用到,但解构器是检查对象引用状态的好方法,加入对象仍然存在引用时,解构器是不会执行的。

注意不要随便重载解构器。

实例方法

实例方法的基本特征就是要传入一个实例对象作为实参。

在 Python 看来,实例方法严格来说属于类属性的一种,实例方法只能通过实例化对象来调用,无法通过类来直接调用。

In [59]: class AClass(object):
...: LOG = 'Define a class'
...: def my_no_actioon_method(self):
...: pass
...: In [60]: dir(AClass)
Out[60]:
['LOG',
'__class__',
'__delattr__',
'__dict__',
'__doc__',
'__format__',
'__getattribute__',
'__hash__',
'__init__',
'__module__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__',
'my_no_actioon_method'] # 通过 dir() 可以找到实例方法存在于类的属性列表中。 In [62]: AClass.my_no_actioon_method()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-62-33bc36ae78f7> in <module>()
----> 1 AClass.my_no_actioon_method() TypeError: unbound method my_no_actioon_method() must be called with AClass instance as first argument (got nothing instead)

如果类直接调用实例方法的话,会触发 TypeError,因为实例方法需要绑定一个实例化对象,这就是 self 存在的意义。所有的含有 self 关键字的属性和方法都只能通过类的实例化对象来调用。所以,一般而言类中定义的实例方法都需要含有 self 形参,当然也可以通过别的方式来解除这种约束,这个我们后面会说到。

In [63]: a_object = AClass()

In [64]: a_object.my_no_actioon_method() #类方法是通过局点标识符来与它的实例化对象绑定的

Python 中的 “抽象方法”

Python 并不支持 Java 中的抽象方法,但可以通过在父类中 ``raise NotImplememtedError 来实现同样的效果。抽象方法的意义在于可以强制的规范在子类中必需定义与父类中方法同名的子类成员方法。

In [74]: class AClass(object):
...: LOG = 'Define a class'
...: def my_method(self):
...: raise NotImplementedError()
...:
...: In [75]: class BClass(AClass):
...: pass
...: In [76]: b_object = BClass() In [77]: dir(b_object)
Out[77]:
['LOG',
'__class__',
'__delattr__',
'__dict__',
'__doc__',
'__format__',
'__getattribute__',
'__hash__',
'__init__',
'__module__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__',
'my_method'] In [78]: b_object.my_method()
---------------------------------------------------------------------------
NotImplementedError Traceback (most recent call last)
<ipython-input-78-49e04830d861> in <module>()
----> 1 b_object.my_method() <ipython-input-74-d6213dfecb1b> in my_method(self)
2 LOG = 'Define a class'
3 def my_method(self):
----> 4 raise NotImplementedError()
5
6 NotImplementedError:

所以通过这种方式,程序员们只能乖乖在子类的重载这个同名方法了,这对程序的规范做了很好的约束。

In [79]: class BClass(AClass):
...: def my_method(self):
...: print "overload the function."
...:
...: In [80]: b_object = BClass() In [81]: b_object.my_method()
overload the function.

实例属性

在类定义中,通过 self 关键字来调用的属性,即属于实例对象的属性,类对象是不能调用的。类属性的创建实际上是通过调用实例方法 __setattr__ 来完成的。x.__setattr__('name', value) <==> x.name = value,除此之外还有内建函数 setattr(),setattr(object, name, value) <==> object.name = value

Python 可以在任意的时间内创建一个命名空间,所以我们可以在创建了实例对象之后定义实例属性,也可以在定义类的时候创建实例属性。后者创建实例属性最重要的方式就是构造器 __init__()

In [98]: class AClass(object):
...: def __init__(self, name, age):
...: self.name = name
...: self.age = age
...: In [99]: a_object = AClass('JMILKFAN', 24) In [101]: a_object.name
Out[101]: 'JMILKFAN' In [102]: a_object.age
Out[102]: 24 In [103]: a_object.sex = 'man' In [104]: a_object.sex
Out[104]: 'man'

当然,我们除了可以在构造器中创建实例属性之外,还可以在类体的任意地方创建,当我们并不推荐这么做。

查看实例属性

查看实例属性和查看类属性是一样的,可以通过 dir()instance.__dict__ 来查看。

EXAMPLE

In [105]: dir(a_object)
Out[105]:
['__class__',
'__delattr__',
'__dict__',
'__doc__',
'__format__',
'__getattribute__',
'__hash__',
'__init__',
'__module__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__',
'age',
'name',
'sex'] In [106]: a_object.__dict__
Out[106]: {'age': 24, 'name': 'JMILKFAN', 'sex': 'man'}

再与查看类属性做一个对比:

In [108]: dir(AClass)
Out[108]:
['__class__',
'__delattr__',
'__dict__',
'__doc__',
'__format__',
'__getattribute__',
'__hash__',
'__init__',
'__module__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__'] In [109]: AClass.__dict__
Out[109]:
dict_proxy({'__dict__': <attribute '__dict__' of 'AClass' objects>,
'__doc__': None,
'__init__': <function __main__.__init__>,
'__module__': '__main__',
'__weakref__': <attribute '__weakref__' of 'AClass' objects>})

NOTE: 对于类或实力对象来说 __dict__ 都是可以手动修改的,但是并不建议我们手动去修改这个属性字典。

我们还能够通过内置方法 getattr() 或实例方法 __getattribute__() 来查看

In [119]: getattr(a_object, 'name')
Out[119]: 'JMILKFAN' In [122]: a_object.__getattribute__('name')
Out[122]: 'JMILKFAN'

实例属性和类属性的区别

类属性与实例无关,类属性的值不会因为实例化对象而改变,除非在实例对象中 显式 的改变了 可变类属性 的值。类属型的值就像是静态成员那样被使用,我们一般采用 class.attr 的方式来调用类属性 attr,除此我们也能够使用实例对象来调用类属性 instance.attr,但前提是实例对象中没有同名的实例属性,否则 instance.attr 中的 attr 就是一个实例属性,返回实例属性的值。相反,实例属性只能由类实例对象来调用。

类属性类似于一个静态属性,在定义类属性的时候会在内存开辟静态空间,用于存放。而且这个静态空间是类的所有实例对象都可以访问的,所以类属性是被所有的由该类实例化的对象共享的。

In [150]: class AClass(object):
...: version = 1.0
...: In [151]: a_object = AClass() In [152]: a_object.version
Out[152]: 1.0 In [153]: AClass.version += 1 In [154]: a_object.version
Out[154]: 2.0

类属性的改变会被实例对象感知。

访问不可变类属性

对于不可变类属性(int/str/tuple)而言,实例对象是无法改变类属性的值得。但如果类属性是可变数据类型,那么实例对象就可以显式的修改其的值。

In [124]: class AClass(object):
...: version = 1.0
...: In [125]: a_object = AClass() In [126]: AClass.version
Out[126]: 1.0 In [127]: a_object.version
Out[127]: 1.0 In [128]: AClass.version = 1.1 In [129]: a_object.version
Out[129]: 1.1 In [130]: AClass.version
Out[130]: 1.1 In [131]: a_object.version
Out[131]: 1.1 In [132]: b_object = AClass() In [133]: b_object.version
Out[133]: 1.1 In [134]: b_object.version = 1.2 # 这里并不没有修改类属性,而是创建了一个新的实例属性 In [135]: AClass.version
Out[135]: 1.1 In [136]: a_object.version
Out[136]: 1.1

从上面的例子可以看出,实例对象可以引用类属性(前提是实例对象没有同名的实例属性),却不能改变类属性,而是创建了一个新的实例属性并覆盖了同名的类属性。

访问可变类属性

In [143]: class AClass(object):
...: aDict = {'name': 'jmilkfan'}
...: In [144]: a_object = AClass() In [146]: AClass.aDict
Out[146]: {'name': 'jmilkfan'} In [147]: a_object.aDict
Out[147]: {'name': 'jmilkfan'} In [148]: a_object.aDict['name'] = 'fanguiju' In [149]: AClass.aDict
Out[149]: {'name': 'fanguiju'}

实例对象可以修改类属性的本质是,实例对象在没有创建一个新的实例属性的基础上进行了修改。

注意:一般认为通过实例对象来修改类属性是危险的,会有很多潜在的风险。所以建议使用类名来修改类属性,而不是实例对象名。

Python 进阶_OOP 面向对象编程_实例属性和方法的更多相关文章

  1. Python 进阶_OOP 面向对象编程_类属性和方法

    目录 目录 类属性 调用类属性 查看类属性 特殊的类属性 类方法 真构造器 __new__ 类属性 在理解类属性之前要先搞清楚 实例属性 和 函数属性 之间的区别: 1. 实例属性:指的是实例化类对象 ...

  2. Python 进阶_OOP 面向对象编程_组合与继承

    #目录 前言 组合 派生 通过继承来覆盖重载方法 最常用的重载场景实例方法的重载 从标准类中派生类方法的重载 前言 我们定义一个类是希望能够把类当成模块来使用,并把类嵌入到我们的应用代码中,与其他的数 ...

  3. Python 进阶_OOP 面向对象编程_类和继承

    目录 目录 类 最简单的类 类方法 构造器 __init__ 创建一个类 实例化一个对象 调用实例的方法和属性 创建子类 使用 super 来调用父类的构造器 实例化子类对象 调用子类的属性和方法 类 ...

  4. Python 进阶_OOP 面向对象编程_静态方法和类方法

    目录 目录 静态方法 类方法 使用函数修饰符来声明静态方法和类方法 静态方法 静态方法仅是类中的函数, 不需要绑定实例, 也就是说静态方法的定义不需要传入 self 参数. 静态方法不属于类的某一个实 ...

  5. Python 进阶_OOP 面向对象编程_self 的实例绑定

    目录 目录 self 和绑定 调用非绑定的方法 self 和绑定 在 Python 中 self 变量是特殊的, 其用于在实例方法中引用该方法所绑定的实例, 换句话说就是 Python 在实例化对象时 ...

  6. Python进阶之面向对象编程

    面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想.OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数. 面向过程的程序设计把计算机 ...

  7. Python进阶之面向对象编程(二)

    Python面向对象编程(二) .note-content {font-family: "Helvetica Neue",Arial,"Hiragino Sans GB& ...

  8. Python进阶之面向对象编程概述

    Python面向对象编程(一) .note-content {font-family: "Helvetica Neue",Arial,"Hiragino Sans GB& ...

  9. Java学习day8面向对象编程2-类的属性和方法

    一.类的属性 1.语法格式 修饰符 类型 属性名 = 初值 说明:修饰符private:该属性只能由该类的方法使用.在同一类内可见.使用对象:变量.方法. 注意:不能修饰类(外部类)    修饰符pu ...

随机推荐

  1. JavaScript DoublyLinkedList

    function DoublyLinkedList() { var Node = function(element) { this.element = element; this.next = nul ...

  2. mybatis插件机制及分页插件原理

    MyBatis 插件原理与自定义插件: MyBatis 通过提供插件机制,让我们可以根据自己的需要去增强MyBatis 的功能.需要注意的是,如果没有完全理解MyBatis 的运行原理和插件的工作方式 ...

  3. scite配置文件及常用设置

    在linux系统中,SciTE的用户设置文件为 ~/.SciTEUser.properties,优先级高于全局配置文件. scite是个不错的IDE工具,只是本人发现,在开发团队中和其他成员的编辑工具 ...

  4. javascript:变量声明&&赋值的提升和函数声明&&定义的提升在不同情况下的表现

    console.log(a); //undefined console.log(show); //函数的定义 show();         //aaa123 var a = 1; function ...

  5. 记录混合APP开发遇到的坑!!

    1.在IOS中给body绑定click事件会失效 2.在IOS中<div contenteditable="true"></div>中点击时可以弹出键盘但是 ...

  6. 最简单的神经网络-感知器-python实现

    import numpy as np import matplotlib.pyplot as plt X=np.array([[1,3,3], [1,4,3], [1,1,1]]) Y=np.arra ...

  7. 20180223-logging模块

    Python的logging模块提供了标准的日志接口,可以通过它存储各种格式的日志,logging的日志可以依次分为debug().info().warning().error().cirtical( ...

  8. CSS 针对谷歌浏览器(Chrome) safari的webkit核心浏览器CSS hack

    @media screen and (-webkit-min-device-pixel-ratio:0) { ul#navUL ul a{padding:8px 2px;word-break:keep ...

  9. centos7系统中忘记了root管理员账号密码的解决方式(转)

    随着计算机的使用越来越普遍,现在的用户都会有多个密码,不是这软件的密码就是那个的,QQ.邮箱.游戏,还有系统的登录密码!每一个密码都不一样!所以越来越多的密码需要去记住!也因为这样,只要其中一个长时间 ...

  10. fork/join并发编程

    Fork & Join 的具体含义 Fork 一词的原始含义是吃饭用的叉子,也有分叉的意思.在Linux 平台中,函数 fork()用来创建子进程,使得系统进程可以多一个执行分支.在 Java ...