python

[TOC]

新旧类问题

​ python2.2后引入了新式类,即显示继承自object的类,为了兼容旧式(经典)类,不显示继承的默认次采用旧式类方式;

​ python3.x 中已经完全去除经典类模式,默认全部采用新式类方式,无论是否显示继承自object。

旧式类

  • 不满足单调性

    如果类C,查找某个属性时,A排在B的前面,那么C的所有子类也要保证这个顺序

  • 使用深度优先搜索方法调用顺序(MRO method resolution order)

  • 不满足本地调用优先级(即根据继承顺序广度依次搜索)

    ​ 如果C(A,B), 本地优先级就是需要按照声明顺序,先找A,再找B

新式类

  • ​ 采用C3算法,C3原本是为Lisp写的

    C3引入了MRO概念,python中任意对象都可以查看起mro,例如:str.mro()

    1. 判断mro要先确定一个线性序列,然后查找路径由由序列中类的顺序决定。所以C3算法就是生成一个线性序列。
    2. 如果继承至一个基类:
      class B(A)
      这时B的mro序列为[B,A]
    3. 如果继承至多个基类
      class B(A1,A2,A3 …)
      这时B的mro序列 mro(B) = [B] + merge(mro(A1), mro(A2), mro(A3) …, [A1,A2,A3])
    4. merge操作就是C3算法的核心。
      遍历执行merge操作的序列,如果一个序列的第一个元素,是其他序列中的第一个元素,或不在其他序列出现,则从所有执行merge操作序列中删除这个元素,合并到当前的mro中;
      merge操作后的序列,继续执行merge操作,直到merge操作的序列为空;
      如果merge操作的序列无法为空,则说明不合法

元类及其应用

​ “元类就是深度的魔法,99%的用户应该根本不必为此操心。如果你想搞清楚究竟是否需要用到元类,那么你就不需要它。那些实际用到元类的人都非常清楚地知道他们需要做什么,而且根本不需要解释为什么要用元类。” —— Python界的领袖 Tim Peters

​ 一个不错的文章

​ python 中万物皆可攀(对象),类class 也是对象,类是创建实例的时候申明的,而元类就是为了创建类而申明的,可以简单理解为类的类;

​ 在我们写下如下代码时,python 解释器会查找是否含有__metaclass__属性,如果有就使用改属性定义的函数或者元类去创建这个类,没有就会使用默认python内置的type元类去创建;

1
2
class SomeClass(object):
pass

那么元类是在什么时候,去调用了__new__ 方法去创建这个类的呢,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 1.类由type创建,创建类时,type的__init__方法自动执行,类() 执行type的 __call__方法(类的__new__方法,类的__init__方法)

# 2.对象由类创建,创建对象时,类的__init__方法自动执行,对象()执行类的 __call__ 方法

In [8]: class mt(type):
...: def __call__(cls): # 重写type的call方法,加入打印注释
...: print 'mt.__call__'
...: super(mt, cls).__call__()
...:

In [11]: class A(object): # 在这个阶段,就会调用元类的__new__方法和__init__方法,这里由于并未重写type的这两个方法,所以看不到打印信息
...: __metaclass__ = mt
...: def __init__(self):
...: print 'A.__init__'
...:

In [12]: a=A() # 这里会调用元类的__call__方法,如果上面的mt.__call__ 不去调用type的__call__的话,我们会发现a是个NoneType,
mt.__call__
A.__init__

​ 在python中,type就是造物主,万象皆是由type而来,比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def fun():
pass

>> fun.__class__
>> <type 'function'>
>> fun.__class__.__class__
>> <type 'type'>

>> num = 1
>> s = 'hello'

>> num.__class__
>> <type 'int'>
>> int.__class__
>> <type 'type'>

>> s.__class__
>> <type 'str'>
>> str.__class__
>> <type 'type'>

​ 如下我们定义了一个mymeta方法,在创建类A的时候会使用我们自定义的方法去创建,当然自定义的方法最终也要返回使用type去创建,毕竟人家是官方的

1
2
3
4
5
6
7
8
9
10
11
def mymeta(class_name, class_bases, class_attr):
'''
class_name 会保存类的名字 Foo
class_bases 会保存类的父类 object
class_attr 会以字典的方式保存所有的类属性
'''
# do something you like
return type(class_name, class_bases, class_attr)

class A(object):
__metaclass__ = mymeta

单例模式

​ 几种方式实现单例模式

​ 使用元类可以轻松实现单例模式,单例模式的好处:

  • 节约内存,所有实例话的变量应用的都是同一块内存
  • 统一管理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import threading

class SingletonType(type):
_instance_lock = threading.Lock()
def __call__(cls, *args, **kwargs):
if not hasattr(cls, "_instance"):
with SingletonType._instance_lock:
if not hasattr(cls, "_instance"):
cls._instance = super(SingletonType,cls).__call__(*args, **kwargs)
return cls._instance

class Foo(metaclass=SingletonType):
def __init__(self,name):
self.name = name


obj1 = Foo('name')
obj2 = Foo('name')
print(obj1,obj2)

迭代器、生成器、可迭代对象

​ 先说可迭代对象,任何可以用iter函数调用的对象,都是可以迭代的,换言之,这个对象里面实现了__iter__或者__getitem__ 所有的容器都实现了至少其中一种方法

​ 迭代器,实现了next方法的可迭代对象都是迭代器

​ 生成器,实现了next方法外,又实现了send方法,return next yielded value or raise StopIteration

​ 故而,生成器是一种特殊的迭代器,可以控制

进程间通信

单工、半双工、全双工

​ 说到通信,无论什么方式的通信,无非分为这三种:单工、半双工、全双工

  1. 单工

    ​ 单工,指在通信全程内线路的端点只能为接受方或发送方,信号只能从单一方向传输,例如:电视,广播

  2. 半双工

    半双工,指在通信的某一时刻,线路中只有一个信号流,从发送方流向接受方,但是双方可以角色互换,可以互相发,但是不能同时发,比如:对讲机
  3. 全双工

    ​ 全双工,指线路中任意时刻的信号流都是双向的,双方互为接受方和发送方,比如:电话

管道

​ 最早的UNIX中的通信方式,如下PIPE 使用python实现,管道是半双工的,两端在开启时既可以是读端,也可以是写端

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
import os, sys

print("The child will write text to a pipe and ")
print("the parent will read the text written by child...")

# file descriptors r, w for reading and writing
r, w = os.pipe()

processid = os.fork() #fork 方法仅能在linux系统上运行,跨平台就用multiprocess

print(processid)

if processid:
# This is the paunameent process
# Closes file descriptor w
os.close(w) #这里必须关闭写fd,因为r端只有收到EOF表识才会返回,如果自己都不关闭,那么即便是子进程写完后退出,关闭了自己的w,那么主进程这里因为自己没关w,所以就会卡死在read
r = os.fdopen(r)
print("Parent reading")
str = r.read()
print("text =", str)
sys.exit(0)
else:
# This is the child process
os.close(r) #关闭读端
w = os.fdopen(w, 'w')
print("Child writing")
w.write("Text written by child...")
w.close()
print("Child closing")
sys.exit(0)
管道只能用与亲缘关系进程间通信,不能用于没有关系的两个进程通信;

​ 数据无格式;

​ <1>当写端存在时,管道中没有数据时,读取管道时将阻塞

​ <2>当读端请求读取的数据大于管道中的数据时,此时读取管道中实际大小的数据

​ <3>当读端请求读取的数据小于管道中的数据时,此时放回请求读取的大小数据

​ <4>当写端不存在,读取管道将返回0

​ <5>当写端存在多个,读取管道将阻塞

​ <6>当写入数据超过限制(一般是系统一个页大小),写入将阻塞

FIFO

​ 先进先出,有名管道,解决了pipe只能在亲缘进程间通信的问题, c实现方式

1
2


如果文章有帮到您,很开心得到您的支持!