常见的三种编程范式
- 函数式编程
- 面向过程编程(函数)
- 面向对象编程(类)
函数式编程:函数可以作为参数传递、修改,或作为返回值,函数内不修改外部变量的状态。
面向过程编程 (函数):根据操作数据的语句块来实现功能。
面向对象编程 (OOP-Object Oriented Programming):把数据和功能结合起来,用称为对象的东西包裹起来组织程序的方法。
面向对象
定义
对象:通过类定义的数据结构实例。
对象包括两个数据成员(类变量和实例变量)和方法。
python一切皆对象,类是对象,实例也是对象。
优点
面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。
继承完全可以理解成类之间的类型和子类型关系。可以节省很多的代码,不需要写,直接使用,衍生出不同的子类,大部分代码一样,部分代码不一样。
面向过程和面向对象
面向过程是一件事"该怎么做",着重于做什么。
面向对象是一件事"该让谁来做",着重于谁去做。
实例
• 实例:具体的某个事物, 某个类型实实在在的一个例子。
• 实例化:创建一个类的实例,类的具体对象。
• 类是一种生产实例的工厂。
属性
• 对象的描述信息,一种状态 -> 变量。
• 车的属性: 品牌、型号、颜色、形状、尺寸。
• 人的属性:身高、体重、姓名、性别、爱好。
方法
• 对象的行为 -> 类中定义的函数
• 车的方法: 驾驶、停车、漂移、越野、运输。
• 人的方法:吃饭、睡觉、运动、打扮、娱乐。
类 (基类、子类)
类(Class): 用来描述具有相同的属性和方法(能做的事)的对象的集合。
它定义了该集合中每个对象所共有的属性和方法。
对象是类的实例。
类的基本特点
1、封装(Encapsulation)
• 在类中对数据的赋值、内部调用对外部用户是透明的。
• 把一些功能的实现细节不对外暴露。
2、继承(Inheritance)
• 继承:即一个派生类(derived class)继承基类(base class)的字段和方法。
• 为实现代码的重用, 一个类可以派生出子类。
• 继承也允许把一个派生类的对象作为一个基类对象对待。
3、多态(Polymorphism)
• 接口重用。
• 一个接口,多种实现(重写)。
类的定义
类名的规范
• 一般首字母大写(大驼峰)
• Person, GoodsInfo
函数的命名
• 由小写字母和下划线组成
• add、book_name
类的定义方式(关键字:class)
• python2 => 经典类和新式类
• python3 => 新式类
class Bus12():
pass
class Bus12(object):
pass
__init__:实例初始化
• 这个名称的开始和结尾都是双下划线。
• 这个方法可以用来对你的对象做一些你希望的初始化任务。
• __init__()方法是一种特殊的方法,被称为类的构造函数或初始化方法,它在类的一个对象被建立
后,自动运行。
__new__:创建一个实例
• 这个名称的开始和结尾都是双下划线。
• 这个方法用来创建一个实例,一般情况下都不会重写。
注意:__new__必须产生和class类型一致的instance, 否则__init__是不会执行的。
执行顺序:先执行__new__,再执行__init__。
类变量与实例变量
类的变量: 由一个类的所有对象(实例)共享使用。
实例的变量: 由类的每个对象/实例拥有。
类和实例都有自己的存储空间。
所有的这些对象都有命名空间,而继承就是由下而上,从左到右原则搜索此树,来寻找属性名称所出现的最低的地方。
可以自由地给类、实例添加或删除属性。
当实例查找属性时,先找自己本身,没有找到就去类上查找,没找到再抛出异常。
类-self
类的实例方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称。
在调用这个方法的时候你不为这个参数赋值,Python会提供这个值。
这个特别的变量指对象本身,按照惯例它的名称是self。
self代表类的实例,而非类,self不必非写成self,self可以不写。
在类的继承时,self代表哪个类,表示的是实例化的类。
类的继承
定义
• 代码:父类Parent/子类Child。
• 子类有__ini__: 执行自己的初始化函数。
• 子类没有__init__: 在子类实例化的时候会自动执行父类的构造函数。
优点
如果需要给Pig/Dog同时增加新的功能时,只需要在Animal中添加即可。
如果父类方法的功能不能满足需求,可以在子类里重写父类的方法。
多态
定义
多态(Polymorphism):“多种状态”,在面向对象语言中,接口的多种不同
实现方式即为多态。
多态其实是一种行为的封装,只需知道所操纵对象能够做的事情(接口)即可。
多态以继承和重写父类方法为前提,多态是调用方法的技巧,不会影响到类的内部设计。
Python是动态语言,动态语言和静态语言最大的差别:动态语言调用实例方法,不检查类型,只要方法存在,参数正确,就可以调用。
优点
1、增加了程序的灵活性、可扩展性。
2、实现接口重用。
多态:同一种事物的多种形态,例如:动物分为人类,猪类等。
多态性:一种调用方式,不同的执行效果。
经典类与新式类
在Python 2.x及以前的版本中,由任意内置类型派生出的类(只要一个内置类型位于类树的某个位
置),都属于“新式类”。
不由任意内置类型派生出的类,则称之为“经典类”。
Python 3.x之后,所有的类都派生自内置类型object(即使没有显示的继承object类型),即所有的类都是“新式类”。
经典类与新式类继承原理
MRO(Method Resolution Order):方法解析顺序。Python 3.x之后,采用c3算法。
MRO是在Python类的多重继承中,当定义了多个同名的方法/属性时,为避免产生歧义,保证用户找到正确的对象所实现的一种算法。
对于定义的每一个类,Python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一
个简单的所有基类的线性顺序列表。
C3算法
合并所有父类的MRO列表并遵循如下三条准则:
• 子类会先于父类被检查。
• 多个父类会根据它们在列表中的顺序被检查。
• 如果对下一个类存在两个合法的选择,选择第一个父类。
静态方法、类方法、实例方法
定义
实例方法不能通过类名调用,但是静态方法和类方法可以通过类名调用。
实例方法:可以通过self访问实例属性。
类方法:可以通过cls访问类属性(希望取的值不受实例影响时使用)
静态方法:不可以访问,通过传值的方式。
属性包装(@propert)
把函数包装成属性,使用的时候用对象名.属性名。
Person类中有一个属性为age,控制age的范围只能在0~150之间。
应用
property广泛应用在类的定义中,可以让调用者写出简短的代码,同时对参数进行必要的检
查。
Python类中的下划线
标识符是用来标识某种对象的名称,以下划线开头的标识符是有特殊意义的。
在命名标识符时,需要遵循一定规则,标识符的第一个字符必须是字母(大小写均可),或者是一个下划线("_")。
以单下划线开头的(_foo)
类:这类成员变量叫做保护变量,意思是只有类对象和子类对象自己能访问到这些变量。
模块:如果你写了代码“from <模块/包名> import *”,那么以“_”开头的模块和包都不会被导入,除非模块或包中的“__all__”列表显式地包含了模块和包。
Python文档:以下划线“_”为前缀的名称(如_spam)应该被视为API中非公开的部分(不管是函数、方法还是数据成员)。此时,应该将它们看作是一种实现细节,在修改它们时无需对外部通知。
以双下划线开头的(__foo)
类:只有类对象自己能访问,连子类对象也不能访问到这个数据。强行访问“对象名._类名__xxx“这样的方式。
模块:不能用“from xxx import *“导入包/模块。
名称(具体为一个方法名)前双下划线(“__“)的用法并不是一种惯例,对解释器来说它有特定
的意义,为了避免与子类定义的名称冲突。
Python文档:“__spam”这种形式(至少两个前导下划线,最多一个后续下划线)的任何标识符将会被“_classname__spam”这种形式原文取代,在这里“classname”是去掉前导下划线的当前类名。
以双下划线开头和结尾的( __foo__ )
代表Python中特殊方法专用的标识。其实,这只是一种惯例,对Python系统来说,这将确保不会
与用户自定义的名称冲突。例如__init__()代表类的构造函数。
常用魔术方法和属性
构造函数(__new__/__init__)
• __new__:创建实例
• __init__:初始化实例
析构函数(__del__)
在实例释放、销毁的时候自动执行的,通常用于做一些收尾工作, 如关闭一些数据库连接,关闭打开的临时文件。
调用方法(__call__)
把类实例化后的对象当做函数来调用的时候自动被调用。
查看类属性和方法
• dir(Person):了解Person类里的变量和方法。
• Person.__dict__ :可以区别变量和方法。
内置属性及其功能
• __dict__:类的属性(包含一个字典,由类的数据属性组成)。
• __doc__:类的文档字符串。
• __name__:类名。
• __module__:类定义所在的模块。
• __bases__:类的所有父类构成元素。
Python元类
对象在实例化的过程中,会调用__init__和__new__方法创建新对象, 一个类本身又是另一个类的实例,用于创建类的类就是元类(Metaclass)。
type创建类
type是Python的内建元类。
语法:type(类名, 父类的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))
创建类:MyClass = type('MyClass', (), {})
函数type实际上是一个元类, type就是Python在背后用来创建所有类的元类(类工厂)如:1 -> int -> type。
编写元类
只需要声明一个继承自type的类。元类的行为继承自type,因此type的子类都可以作为元类。
元类的主要目的就是为了当创建类时能够自动地改变类:
• 拦截类的创建。
• 修改类,返回修改之后的类。
指定元类
1、默认元类为type:
class Foo(Bar): __metaclass__ = something
class Foo(Bar, metaclass=something):pass
2、Python会在类的定义(父类)中寻找__metaclass__属性,找到后,Python就会用它来创建类
Foo,如果没有找到,就会用内建的type来创建这个类。
3、 __metaclass__中放置可以创建一个类的东西(元类)。
抽象基类
python中并没有提供抽象类与抽象方法,但是提供了内置模块abc(abstract base class)来模拟
实现抽象类。
ABC,Abstract Base Class(抽象基类),主要定义了基本类和最基本的抽象方法,可以为子类
定义共有的API,不需要具体实现。
抽象基类提供了逻辑和实现解耦的能力,即在不同的模块中通过抽象基类来调用。
ABC类实际上是一个元类。
Python自省
什么是自省
检查某些事物以确定它是什么、它知道什么以及它能做什么。
4个方法
• getattr(obj,'name'): 获取成员,根据字符串去获取obj对象里对应方法的内存地址。
• hasattr(obj,'name'): 检查是否含有成员,判断一个对象obj里是否有对应的name_str字符串。
• setattr(obj,'age', 20): 设置成员。
• delattr(obj,'name'): 删除成员。