Python面向对象初识

面相对象初识

  • 面向过程编程
    面向过程编程即流程式编程,根据问题进行分析,抽象出解决问题所需要的步骤,并按步骤逐步进行函数调用解决问题。

  • 函数式编程
    函数式编程是一种编程范式,即给定输入值,经过函数处理后给出返回值,即为函数式编程。比起指令式编程,函数式编程更加强调程序执行的结果而非执行的过程,倡导利用若干简单的执行单元让计算结果不断渐进,逐层推导复杂的运算,而不是设计一个复杂的执行过程。

  • 面向对象编程
    面向对象编程是种具有对象概念的程序编程典范,同时也是一种程序开发的抽象方针。它可能包含数据、属性、代码与方法。

类与对象

  • 概念
    定义了一件事物的抽象特点,类的定义包含了数据的形式以及对数据的操作。

  • 意义
    类的出现,为面向对象编程的三个最重要的特性,即封装性、继承性、多态性,提供了实现的手段。

  • 作用
    属性引用、实例化

  • 声明类
1
2
3
class Foo:
"description of class"
pass

对象

  • 概念
    对象即类的实例

  • 意义
    类相当于是蓝图,定义了一件特定事物的抽象特点,而对象则是类的真实实例,实现了类定义。

  • 作用
    属性引用

  • 创建对象

1
2
3
4
5
6
class Foo:
"description of class"
def __init__(self, x):
self.x = x

foo = Foo() # 类的实例化,python中类的实例化会自动调用类的__init__方法

属性与方法

  • 概念
    在Python中 , 我们将静态属性 就称为属性, 将动态属性就称为方法, 以变量表示属性, 以函数表示方法

  • 调用
    类/实例调用属性/方法的方式为:类名/实例名 . 属性名/方法名

  • 特殊的类属性

属性名 说明
_dict_ 查看类/实例成员, 返回字典
_name_ 查看类名
_doc_ 查看类的描述信息, 即类的注释
_base_ 查看当前类的第一个父类
_bases_ 查看当前类的所有父类, 返回元组
_module_ 查看当前类所在模块
_class_ 查看当前实例的父类

构造方法 & 析构方法

  • 构造方法
    \__init\__被称作构造方法,其作用是用于对类进行初始化,如果需要对类设置属性,则可以在构造方法中进行初始化设置。

  • 析构方法
    \__del\__被称作析构方法,当对象的生命周期结束时,它会自动地被调用运行。它最主要的目的在于,清空并释放对象先前创建或是占用的存储器资源。


命名空间

命名空间它表示着一个标识符的可见范围,在定义类的时候会产生类的命名空间,同样在类实例化对象之后,对象也会对应产生命名空间。当调用类/实例的属性/方法时,Python解释器首先会到该实例的命名空间中去找对应属性/方法,如果没有找到则继续向上去类的命名空间去找,如果都没有找到对应调用的属性/方法,则抛出异常。


类/实例中的属性/方法绑定关系

  • Python作为一种动态语言,其所有的赋值机制都是通过动态绑定实现。
  • 类中的属性/方法可供其创建的所有实例使用
  • 实例中的属性/方法只适用于实例本身

对象交互&类的组合

  • 对象交互
1
2
3
4
5
6
7
8
9
10
11
12
class Foo:
def __init__(self, x):
self.x = x
def interactive(self, other):
print("【{}】 interactive 【{}】".format(self.x, other.x))

obj_a = Foo('obj_a')
obj_b = Foo('obj_b')
obj_a.interactive(obj_b)

# 输出
【obj_a】 interactive 【obj_b】
  • 类的组合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 传参
class Person:
def __init__(self, name):
self.name = name

class Worker:
def __init__(self, person):
self.person = person

person = Worker(Person('Yang'))

# 定义
class Person:
def __init__(self, name):
self.name = name

class Worker:
def __init__(self, person):
self.person = Person('Yang')

worker = Worker()

metaclass(元类)

https://docs.python.org/zh-cn/3.8/reference/datamodel.html#metaclasses

概念及作用

  • 概念
    一种用于创建类的类。
    类定义包含类名、类字典和基类列表。
    元类负责接受上述三个参数并创建相应的类。
    大部分面向对象的编程语言都会提供一个默认实现。
    Python 的特别之处在于可以创建自定义元类。
    大部分用户永远不需要这个工具,但当需要出现时,元类可提供强大而优雅的解决方案。
  • 作用
    它们已被用于记录属性访问日志、添加线程安全性、跟踪对象创建、实现单例,以及其他许多任务。

元类的原理及定义类的执行过程

  • 元类的原理
    默认情况下,类是使用 type() 来构建的。类体会在一个新的命名空间内执行,类名会被局部绑定到 type(name, bases, namespace) 的结果。
    类创建过程可通过在定义行传入 metaclass 关键字参数,或是通过继承一个包含此参数的现有类来进行定制。

  • 类定义的执行过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1、解析 MRO 条目:
如果在类定义中出现的基类不是 type 的实例,则使用 __mro_entries__ 方法对其进行搜索,当找到结果时,它会以原始基类元组做参数进行调用。

2、确定适当的元类:
在类定义时确定元类的流程:
①如果没有基类且没有显式指定元类,则使用 type();
②如果给出一个显式元类且不是 type() 的实例,则其会被直接用作元类;
③如果给出一个 type() 的实例作为显式元类,或是定义了基类,则使用最近派生的元类。

3、准备类命名空间:
在类定义之时确定元类的命名空间原则:
①如果元类具有 __prepare__ 属性,它会以 namespace = metaclass.__prepare__(name, bases, **kwds) 的形式被调用(其中如果有任何额外的关键字参数,则应来自类定义)。 __prepare__ 方法的实现应当为 classmethod()。
②如果元类没有 __prepare__ 属性,则类命名空间将初始化为一个空的有序映射。

4、执行类主体:
类主体会以(类似于) exec(body, globals(), namespace) 的形式被执行。普通调用与 exec() 的关键区别在于当类定义发生于函数内部时,词法作用域允许类主体(包括任何方法)引用来自当前和外部作用域的名称。
但是,即使当类定义发生于函数内部时,在类内部定义的方法仍然无法看到在类作用域层次上定义的名称。类变量必须通过实例的第一个形参或类方法来访问,或者是通过隐式词法作用域的 __class__ 引用。

5、创建类对象:
一旦执行类主体完成填充类命名空间,将通过调用 metaclass(name, bases, namespace, **kwds) 创建类对象(此处的附加关键字参数与传入 __prepare__ 的相同)。

如果类主体中有任何方法引用了 __class__ 或 super,这个类对象会通过零参数形式的 super(). __class__ 所引用,这是由编译器所创建的隐式闭包引用。这使用零参数形式的 super() 能够正确标识正在基于词法作用域来定义的类,而被用于进行当前调用的类或实例则是基于传递给方法的第一个参数来标识的。

当使用默认的元类 type 或者任何最终会调用 type.__new__ 的元类时,以下额外的自定义步骤将在创建类对象之后被发起调用:
①首先,type.__new__ 将收集类命名空间中所有定义了 __set_name__() 方法的描述器;
②接下来,所有这些 __set_name__ 方法将使用所定义的类和特定描述器所赋的名称进行调用;
③最后,将在新类根据方法解析顺序所确定的直接父类上调用 __init_subclass__() 钩子。

在类对象创建之后,它会被传给包含在类定义中的类装饰器(如果有的话),得到的对象将作为已定义的类绑定到局部命名空间。

当通过 type.__new__ 创建一个新类时,提供以作为命名空间形参的对象会被复制到一个新的有序映射并丢弃原对象。这个新副本包装于一个只读代理中,后者则成为类对象的 __dict__ 属性。

单例模式的四种方式

  • __new__
1
2
3
4
5
6
7
class Singleton(object):
"""单例模式"""
_instance = None
def __new__(cls, *args, **kw):
if not cls._instance:
cls._instance = super(Singleton, cls).__new__(cls, *args, **kw)
return cls._instance
  • 模块导入
1
2
3
4
5
6
7
8
9
10
# Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc文件,而不会再次执行模块代码。因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得单例对象

class My_Singleton(object):
def foo(self):
pass
my_singleton = My_Singleton()

# 将上面的代码保存在文件 mysingleton.py 中,然后这样使用:
from mysingleton import my_singleton
my_singleton.foo()
  • 装饰器(decorator)
1
2
3
4
5
6
7
8
9
10
11
12
13
from functools import wraps

def singleton(cls):
instances = {}
@wraps(cls)
def getinstance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return getinstance
@singleton
class MyClass(object):
pass
  • 元类(metaclass)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class SingletonType(object):
"""docstring for Singleton"""
def __init__(self, *args,**kwargs):
super(SingletonType, self).__init__(*args,**kwargs)
def __call__(cls,*args,**kwargs):
obj = cls.__new__(cls,*args,**kwargs)
cls.__init__(obj,*args,**kwargs)
return obj
class Foo(metaclass=SingletonType):
"""docstring for Foo"""
def __init__(self, name):
self.name = name
def __new__(cls,*args,**kwargs):
return object.__new__(cls)
obj = Foo('xxx')