面向对象是一种编程方式, 此编程方式的实现是基于对 类 和 对象 的使用
类 是一个模板, 模板中包装了多个函数供使用(可以讲多函数中公用的变量封装到对象中)
对象, 根据模板创建的实例(即: 对象), 实例用于调用被包装在类中的函数
面向对象三大特性: 封装继承和多态
本篇将详细介绍 Python 类的成员成员修饰符类的特殊成员
类的成员
类的成员可以分为三大类: 字段方法和属性
注: 所有成员中, 只有普通字段的内容保存对象中, 即: 根据此类创建了多少对象, 在内存中就有多少个普通字段而其他的成员, 则都是保存在类中, 即: 无论对象的多少, 在内存中只创建一份
一字段
字段包括: 普通字段和静态字段, 他们在定义和使用中有所区别, 而最本质的区别是内存中保存的位置不同,
普通字段属于对象
静态字段属于类
字段的定义和使用
由上述代码可以看出普通字段需要通过对象来访问静态字段通过类访问, 在使用上可以看出普通字段和静态字段的归属是不同的其在内容的存储方式类似如下图:
由上图可是:
静态字段在内存中只保存一份
普通字段在每个对象中都要保存一份
应用场景: 通过类创建对象时, 如果每个对象都具有相同的字段, 那么就使用静态字段
二方法
方法包括: 普通方法静态方法和类方法, 三种方法在内存中都归属于类, 区别在于调用方式不同
普通方法: 由对象调用; 至少一个 self 参数; 执行普通方法时, 自动将调用该方法的对象赋值给 self;
类方法: 由类调用; 至少一个 cls 参数; 执行类方法时, 自动将调用该方法的类复制给 cls;
静态方法: 由类调用; 无默认参数;
方法的定义和使用
相同点: 对于所有的方法而言, 均属于类 (非对象) 中, 所以, 在内存中也只保存一份
不同点: 方法调用者不同调用方法时自动传入的参数不同
三属性
如果你已经了解 Python 类中的方法, 那么属性就非常简单了, 因为 Python 中的属性其实是普通方法的变种
对于属性, 有以下三个知识点:
属性的基本使用
属性的两种定义方式
1 属性的基本使用
属性的定义和使用
由属性的定义和调用要注意一下几点:
定义时, 在普通方法的基础上添加 @property 装饰器;
定义时, 属性仅有一个 self 参数
调用时, 无需括号
方法: foo_obj.func()
属性: foo_obj.prop
注意: 属性存在意义是: 访问属性时可以制造出和访问字段完全相同的假象
属性由方法变种而来, 如果 Python 中没有属性, 方法完全可以代替其功能
实例: 对于主机列表页面, 每次请求不可能把数据库中的所有内容都显示到页面上, 而是通过分页的功能局部显示, 所以在向数据库中请求数据时就要显示的指定获取从第 m 条到第 n 条的所有数据(即: limit m,n), 这个分页的功能包括:
根据用户请求的当前页和总数据条数计算出 m 和 n
根据 m 和 n 去数据库中请求数据
View Code
从上述可见, Python 的属性的功能是: 属性内部进行一系列的逻辑计算, 最终将计算结果返回
2 属性的两种定义方式
属性的定义有两种方式:
装饰器 即: 在方法上应用装饰器
静态字段 即: 在类中定义值为 property 对象的静态字段
装饰器方式: 在类的普通方法上应用 @property 装饰器
我们知道 Python 中的类有经典类和新式类, 新式类的属性比经典类的属性丰富( 如果类继 object, 那么该类是新式类 )
经典类, 具有一种 @property 装饰器(如上一步实例)
View Code
新式类, 具有三种 @property 装饰器
View Code
注: 经典类中的属性只有一种访问方式, 其对应被 @property 修饰的方法
新式类中的属性有三种访问方式, 并分别对应了三个被 @property@方法名. setter@方法名. deleter 修饰的方法
由于新式类中具有三种访问方式, 我们可以根据他们几个属性的访问特点, 分别将三个方法定义为对同一个属性: 获取修改删除
实例
静态字段方式, 创建值为 property 对象的静态字段
当使用静态字段的方式创建属性时, 经典类和新式类无区别
View Code
property 的构造方法中有个四个参数
第一个参数是方法名, 调用 对象. 属性 时自动触发执行方法
第二个参数是方法名, 调用
对象. 属性 = XXX
时自动触发执行方法
第三个参数是方法名, 调用 del 对象. 属性 时自动触发执行方法
第四个参数是字符串, 调用
对象. 属性.__doc__
, 此参数是该属性的描述信息
View Code
由于静态字段方式创建属性具有三种访问方式, 我们可以根据他们几个属性的访问特点, 分别将三个方法定义为对同一个属性: 获取修改删除
实例
注意: Python web 框架 Django 的视图中 request.POST 就是使用的静态字段的方式创建的属性
Django 源码
所以, 定义属性共有两种方式, 分别是装饰器和静态字段, 而装饰器方式针对经典类和新式类又有所不同
类成员的修饰符
类的所有成员在上一步骤中已经做了详细的介绍, 对于每一个类的成员而言都有两种形式:
公有成员, 在任何地方都能访问
私有成员, 只有在类的内部才能方法
私有成员和公有成员的定义不同: 私有成员命名时, 前两个字符是下划线(特殊成员除外, 例如:__init____call____dict__等)
- class C:
- def __init__(self):
self.name = 公有字段
self.__foo = "私有字段"
私有成员和公有成员的访问限制不同:
静态字段
公有静态字段: 类可以访问; 类内部可以访问; 派生类中可以访问
私有静态字段: 仅类内部可以访问;
公有静态字段
私有静态字段
普通字段
公有普通字段: 对象可以访问; 类内部可以访问; 派生类中可以访问
私有普通字段: 仅类内部可以访问;
ps: 如果想要强制访问私有字段, 可以通过 对象._类名__私有字段明 访问(如: obj._C__foo), 不建议强制访问私有成员
公有字段
私有字段
方法属性的访问于上述方式相似, 即: 私有成员只能在类内部使用
ps: 非要访问私有属性的话, 可以通过 对象._类__属性名
类的特殊成员
上文介绍了 Python 的类成员以及成员修饰符, 从而了解到类中有字段方法和属性三大类成员, 并且成员名前如果有两个下划线, 则表示该成员是私有成员, 私有成员只能由类内部调用无论人或事物往往都有不按套路出牌的情况, Python 的类成员也是如此, 存在着一些具有特殊含义的成员, 详情如下:
1. __doc__
表示类的描述信息
View Code
2. __module__ 和 __class__
__module__ 表示当前操作的对象在那个模块
__class__ 表示当前操作的对象的类是什么
lib/aa.py
index.py
3. __init__
构造方法, 通过类创建对象时, 自动触发执行
View Code
4. __del__
析构方法, 当对象在内存中被释放时, 自动触发执行
注: 此方法一般无须定义, 因为 Python 是一门高级语言, 程序员在使用时无需关心内存的分配和释放, 因为此工作都是交给 Python 解释器来执行, 所以, 析构函数的调用是由解释器在进行垃圾回收时自动触发执行的
View Code
5. __call__
对象后面加括号, 触发执行
注: 构造方法的执行是由创建对象触发的, 即: 对象 = 类名() ; 而对于 __call__ 方法的执行是由对象后加括号触发的, 即: 对象() 或者 类()()
View Code
6. __dict__
类或对象中的所有成员
上文中我们知道: 类的普通字段属于对象; 类中的静态字段和方法等属于类, 即:
View Code
7. __str__
如果一个类中定义了__str__方法, 那么在打印 对象 时, 默认输出该方法的返回值
View Code
8__getitem____setitem____delitem__
用于索引操作, 如字典以上分别表示获取设置删除数据
- #!/usr/bin/env 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] = wupeiqi # 自动触发执行 __setitem__
- del obj[k1] # 自动触发执行 __delitem__
- 9__getslice____setslice____delslice__
该三个方法用于分片操作, 如: 列表
- #!/usr/bin/env python
- # -*- coding:utf-8 -*-
- class Foo(object):
- def __getslice__(self, i, j):
- print __getslice__,i,j
- def __setslice__(self, i, j, sequence):
- print __setslice__,i,j
- def __delslice__(self, i, j):
- print __delslice__,i,j
- obj = Foo()
- obj[-1:1] # 自动触发执行 __getslice__
- obj[0:1] = [11,22,33,44] # 自动触发执行 __setslice__
- del obj[0:2] # 自动触发执行 __delslice__
- 10. __iter__
用于迭代器, 之所以列表字典元组可以进行 for 循环, 是因为类型内部定义了 __iter__
第一步
第二步
第三步
以上步骤可以看出, for 循环迭代的其实是 iter([11,22,33,44]) , 所以执行流程可以变更为:
- #!/usr/bin/env python
- # -*- coding:utf-8 -*-
- obj = iter([11,22,33,44])
- for i in obj:
- print i
For 循环语法内部
11. __new__ 和 __metaclass__
阅读以下代码:
- class Foo(object):
- def __init__(self):
- pass
- obj = Foo() # obj 是通过 Foo 类实例化的对象
上述代码中, obj 是通过 Foo 类实例化的对象, 其实, 不仅 obj 是一个对象, Foo 类本身也是一个对象, 因为在 Python 中一切事物都是对象
如果按照一切事物都是对象的理论: obj 对象是通过执行 Foo 类的构造方法创建, 那么 Foo 类对象应该也是通过执行某个类的 构造方法 创建
- print type(obj) # 输出: 表示, obj 对象由 Foo 类创建
- print type(Foo) # 输出: 表示, Foo 类对象由 type 类创建
所以, obj 对象是 Foo 类的一个实例, Foo 类对象是 type 类的一个实例, 即: Foo 类对象 是通过 type 类的构造方法创建
那么, 创建类就可以有两种方式:
a). 普通方式
- class Foo(object):
- def func(self):
- print hello wupeiqi
b). 特殊方式(type 类的构造函数)
- def func(self):
- print hello wupeiqi
- Foo = type(Foo,(object,), {func: func})
- #type 第一个参数: 类名
- #type 第二个参数: 当前类的基类
- #type 第三个参数: 类的成员
== 类 是由 type 类实例化产生
那么问题来了, 类默认是由 type 类实例化产生, type 类中如何实现的创建类? 类又是如何创建对象?
答: 类中有一个属性 __metaclass__, 其用来表示该类由 谁 来实例化创建, 所以, 我们可以为 __metaclass__ 设置一个 type 类的派生类, 从而查看 类 创建的过程
View Code
来源: http://www.bubuko.com/infodetail-2517058.html