You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
定义了相同方法(包括魔法方法)的类,可以归为同一种类型(比如 list、set 都是可迭代对象,都可以调用 extend),以相同的方式调用该方法、也可以 for 循环迭代;
缺陷是作为动态语言无法在编译时发现错误。
classCat(object):
defsay(self):
print("i am a cat")
classDog(object):
defsay(self):
print("i am a fish")
classDuck(object):
defsay(self):
print("i am a duck")
animal_list= [Cat, Dog, Duck]
foranimalinanimal_list:
animal().say() # 都可以“叫”,都可以当它是动物来用,而不必区分是哪种动物
This discussion was converted from issue #20 on June 09, 2023 03:08.
Heading
Bold
Italic
Quote
Code
Link
Numbered list
Unordered list
Task list
Attach files
Mention
Reference
Menu
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
-
变量的本质
在 Python 中变量的本质是一个指针(类比为可贴在任何物品上的 标签,Java 中则可以类比为盒子)。
传参错误问题
向函数传入 list、dict,对象可能会被修改。
对象和类型
Python 中的一切皆对象,包括 class、function 等,具体表现在:
可赋值给变量
可添加到集合
可作为函数参数
可作为函数返回值
其中对象的特征:身份(即对象在内存中的地址,id)、类型、值。
type、object、class
type:
可以创建所有对象(类、函数、集合)的类,或返回对象的类型(
type(Object) == type
)type 也是对象(自身类的对象),且作为类时 type 继承于 object:
type.__bases__ == (object, )
object:
object 是 type 的实例,type 是 object 的类:
type(object) == type
object 是所有类的基类(
xxx.__bases__
),自定义类默认继承 object:object.__bases__ == ()
常见内置类型
None:全局只有一个
数值:int、float、complex、bool
迭代:for ... in ...
序列:list、bytes、range、tuple、str、array
映射:dict
集合:set、fronzenset
上下文:with
其他:模块(import)、class 和 object、函数、方法、代码、object、type、elipsis、notimplemented
isinstance 与 type
使用 isinstance 与 type 都可以判断对象的类型,但更推荐使用 isinstance,会检查对象类的继承链。
鸭子类型(Duck Typing)
变量可以指向任何类型的对象(不需要继承,也不必考虑多态);
定义了相同方法(包括魔法方法)的类,可以归为同一种类型(比如 list、set 都是可迭代对象,都可以调用
extend
),以相同的方式调用该方法、也可以for
循环迭代;缺陷是作为动态语言无法在编译时发现错误。
垃圾收集(Garbage Collection)
Python 的垃圾收集是基于引用计数、标记-清除机制、分代回收。
其中魔法方法
__del__
定义垃圾收集时执行的操作:引用计数
PyObject
存在于所有对象中,其中ob_refcnt
即为引用计数。当一个对象有新的引用时,
ob_refcnt
即会增加,而引用它的对象被删除,ob_refcnt
就会减少。当引用计数为 0 时,表示对象生命结束,内存会被回收。
这种做法简单、实时,但维护引用计数需要消耗资源,且可能存在循环引用的问题。
标记 - 清除
即先按需分配,在没有空闲内存时,从寄存器和程序栈上的引用出发,遍历以对象为节点、以引用为边构成的图,把所有可以访问到的对象打上标记,然后对内存空间清扫,把所有没标记的对象释放。
分代回收
将系统中的所有内存块根据其存活时间划分为不同的集合(即“代”),垃圾收集频率随着“代”的存活时间(使用经过垃圾回收次数度量)的增大而减小。Python 默认定义了三代对象集合,索引数越大,对象存活时间越长。
假设当某些内存块M经过了 3 次垃圾收集的清洗之后还存活时,就将内存块 M 划到一个集合 A 中去,而新分配的内存都划分到集合 B 中去。
当垃圾收集开始工作时,大多数情况都只对集合 B 进行垃圾回收,而对集合 A 进行垃圾回收要隔相当长一段时间后才进行,从而减少垃圾收集机制需要的内存,提高效率。
在这个过程中,集合 B 中的某些内存块由于存活时间长而会被转移到集合 A 中;
集合 A 中实际上也存在一些垃圾,这些垃圾会因为这种分代的机制而被延迟回收。
魔法方法(Magic Method)
在 Python 中使类获得某种功能不必通过继承基类、实现方法,而是实现魔法方法(协议);
魔法方法属于数据模型的一种特性,可以在任何对象中定义;
魔法方法对 Python 原生的类型支持更好,应尽量使用原生;
可迭代对象
__iter__
会优先于__getitem__
且性能更好。常用魔法方法及其实现特性
__getitem__
:可遍历、切片__len__
:取长度__str__
:输出类信息(__repr__
于交互式环境自动调用)另外更多魔法方法用法见:https://rszalski.github.io/magicmethods/
实现类的比较操作
和其他语言(Java、C++ 的运算符重载)类似,Python 也可以通过重写魔法方法使任意类型支持运算和比较操作,下面以比较不同图形的面积大小为例:
加减乘除等运算也支持这样的操作,感兴趣的朋友可以试试。
实现上下文管理对象
在进行文件读写操作时常会用到 with 语句,作用是进入上下文作用域,作用域内可使用 with 语句返回的对象:
退出作用域时不需要使用
f.close()
手动关闭文件对象,这在open
类型已经内部定义好:当退出作用域时自动关闭文件对象。实现
__enter__
和__exit__
方法后,可用with
语句划分作用域,使任意类型都支持这种上下文管理:又如下面建立 SSH 连接远程执行命令的例子:
执行结果:
另外通过 contextlib 模块可实现上下文管理器的简化
继承机制(Inheritance)
抽象基类(Abstract Base Class)
类似 Java 中的接口(定义子类共有的方法且必须被实现),不能实例化;
当希望判定某个对象的类型,或强制某个子类必须实现某方法时,可以通过定义抽象基类让子类继承;
collections.abc
模块包含了内部定义的抽象基类,也可以直接导入 abc 自己实现;虽然提供了抽象基类功能,但还是应该尽可能重用鸭子类型。
实例:
super 函数
子类继承自父类,有时不需要在子类中执行变量赋值:
self.name = name
,只需要执行父类初始化方法即可;super 执行顺序:MRO 顺序(而不是调用父类构造方法)
多继承设计
建议使用 Mixin 模式:可以更合理地组织代码、复用代码
Mixin 类表示一种功能,功能应尽量单一(表示这个父类作为功能添加到子类,而不起父类作用,类似 Java 的 Interface);
不和基类关联,可以和任意基类组合,基类可以不和 Mixin 关联就能初始化成功;
Mixin 类不依赖于子类的实现;
子类没有基础 Mixin 类也可以照常工作,只是缺少部分功能;
在 Mixin 中不要使用 super 这种用法(不能按照 MRO 顺序)。
例如此处的 Airplane 继承了 Vehicle 和 PlaneMixin,但从含义上 PlaneMixin 表示混入,Airplane 实际上只是属于 Vehicle 的一种。
扩展内置数据结构
通过继承和重写构造方法的方式可以对 Python 内置的数据结构进行功能扩展,如下面正整数元组的例子:
执行结果:
自省机制(Introspection)
自省是指对象创建后可以通过一定的机制查询和修改其内部结构(如运行时能够获得对象的类型、为对象添加方法)。
通过名称调用方法
有时获取到方法的名称(例如客户端 get 或 post 传来的字符串数据),要根据这个名称在后端调用相应的方法,一般的做法是使用 if...else 判断。但当程序规模变大,使用 if...elif...else 的判断逻辑就会线性递增,既不方便也不美观。
使用 hasattr、getattr 的方法(也有人称之为“反射”)可以通过字符串在当前的类或模块中获取函数对象:
其中 hasattr 是判断 self(即当前对象)中是否有名为 "get_" + key 的方法,getattr 则获取该方法的对象来执行。
当要从 当前模块中 获取函数对象,可以:
类的属性与方法
类属性和实例属性
定义在类和实例中的变量与方法
查找顺序:
由下而上,先检查实例是否存在该属性,不存在再从类中查找;
多继承时,采用 C3 算法在父类中查找。
静态方法、类方法、对象方法
数据封装与私有属性
以双下划线开头的私有属性不可以直接通过对象直接访问,而应该定义 get、set 方法进行有条件的读写;
以单下划线开头的私有属性不能用
from module import *
导入,其他方面和公有属性访问方式一样;私有属性仍然可以通过
obj._classname__field
访问,所以不是绝对安全的;利用这个机制,可以解决类继承后私有变量重名的问题。
__slots__
属性需要创建大量实例时,定义
__slots__
可以限制类实例的属性(定义后不支持动态绑定属性)。执行结果:
除此之外对比
dir(s1)
和dir(s2)
也可以发现可见 s2 少了__dict__
(实现动态绑定、解除属性)和__weakref__
(弱引用,引用时不增加引用计数),因此能节省大量内存。对象属性管理与元类编程
Property
在类中实现内置的
property
方法可以动态地获取、设置、删除类/对象属性:执行结果:
其中
property
方法也可以通过装饰器的方式实现,加入property
装饰器的方法将转变为特性,可以使该方法支持以字段的方式访问),但默认只可读:要使属性可写,只需再实现
setter
装饰器:使用描述符做类型检查
为了使代码更好维护,不建议直接访问成员变量,而使用 property 实现 get、set 方法,通过代码逻辑控制访问权限(使用描述符定义 getter、setter 方法,可以限制类属性可接收的类型,判断失败抛出异常):
执行结果:
getattr 与 getattribute
属性描述符
利用属性描述符做参数类型检查(类似 Django 的做法)
__new__
与__init__
__new__
是用来控制对象的生成过程, 在对象生成之前;__init__
是用来完善对象的,如果new方法不返回对象, 则不会调用 init 方法。元类编程
类的本质是对象,
type
是创建类的类;元类是创建类的类:对象 <- class(对象) <- type;
使用元类可以控制类的实例化过程(类定义中指定 metaclass 属性,通过 metaclass 去创建类);
类的实例化过程:首先寻找 metaclass,去创建类对象、实例;
一般不直接使用
type
,而创建一个 MetaClass 继承type
,再在要实例化的类中指定metaclass
属性为这个 MetaClass。实例:实现简易 ORM
Beta Was this translation helpful? Give feedback.
All reactions