Python高级编程

过滤删选(解析比filter快)

d = [-1, 3, 2, 1, 0, 10]
r = [x for x in d if d > 3] ==> [10]
dd = {'a': 1, 'b': -1}
rr = {k: v for k, v in dd.items() if v > 0} ==> {'a': 1}

集合对象,减少[0][1]...的使用,提高代码的可维护性

method-1:
index_name_1, index_name_2 ... = xrange(2)
s = ('a', 'b' ...)
s[index_name_1] ==> 'a'
s[index_name_2] ==> 'b'

method-2:
from collections import namdtuple
s = namdtuple('tuple_name', ['index_name_1', 'index_name_2'...])
obj = s('a', 'b' ... )
obj.index_name_1 ==> 'a'
obj.index_name_2 ==> 'b'

对一定结构数据的计数统计

from collections import Counter
a = [1, 2, 3, 3, 4, 1]
c = Counter(a)
a[1] ==> 2
a[2] ==> 1
a[3] ==> 2
a[4] ==> 4
c.most_common(1) ==> [(4, 4)]

Counter方法详细介绍:http://www.pythoner.com/205.html

dict中按照value值排序

method-1:
a = {'a': 2, 'b': 1, 'c': 5}
sorted(zip(a.itervalues(), a.iterkeys())) ==> [(1, 'b'), (2, 'a'), (5, ')]

method-2:
a = {'a': 2, 'b': 1, 'c': 5}
sorted(a.iteritems(), key=lambda x: x[1]) ==> [('b', 1), ('a', 2), ('c', 5)]

如何快速找到多个字典中的公共键(key)

method-1:(只是试用有限个数)
s1 = {'a': 1, 'b';2}
s2 = {'b': 1, 'c': 1}
s3 = {'a': 2, 'b': 3, 'd': 1}
s1.viewkeys() & s2.viewkeys() & s3.viewkeys() ==> {'b'}

method-2:
reduce(lambda a, b: a & b, map(dict.viewkeys, [s1, s2, s3])) ==> {'b'}

如何让字典保持有序(collections.OrderedDict)

from time import time
from random import randint
from collections import OrderedDict

d = OrderedDict()
players = list('abcdefgh')
start = time()
for i in xrange(7):
    raw_input()
    p = players.pop(randint(0, 7 - i)) 
    end = time()
    print i + 1, p, end - start
    d[p] = (i + 1, end - start)
    
for y in d:
    print y, d[y]

==> p (1, time)
    p (2, time)
    p (3, time)
    p (4, time)
    p (5, time)
    p (6, time)
    p (7, time)

记录历史记录,collections.deque和pickle的使用

from random import randint
from collections import deque
import pickle

N = randint(1, 100)

try:
    history = pickle.load(open('history.txt'))
except:
    history = deque([], 5)

def guess(k):
    if k == N:
        print 'right'
        return True
    if k < N:
        print '%s is less then N' % k
    else:
        print '%s is greater then N' % k
    return False

while True:
    line = raw_input('plase input a number:')
    if line.isdigit():
        k = int(line)
        history.append(k)
        pickle.dump(history, open('history.txt', 'w'))
        if guess(k):isalnum
            break
    elif line == 'history' or line == 'h?':
        print list(history)

注:deque列表对象,有两个参数,初始队列和最大长度,并有append和appendleft方法,分别是从后插入和从前插入
pickle的作用是将python对象存入文件,用法与json的使用的方法很相似,有dump和load方法等
使用isdigit判断字符串是否可以转为整型,另外还有很多字符串内置的判断函数

构造可迭代对象,collections.Iterable和collections.Iterator的使用

from collections import Iterable, Iterator

class Demo1(Iterator):
    def __init__(self, cities):
        self.cities = cities
        self.index = 0 
    
    def next(self):
        if self.index == len(self.cities):
            raise StopIteration
        city = self.cities[self.index]
        self.index += 1
        return city

class Demo2(Iterable):
    def __init__(self, cities):
        self.cities = cities

    def __iter__(self):
        return Demo1(self.cities)

a = Demo2(['a','b', 'c'])
for each in a:
    print each 

注:在构造可迭代对象时必须使用raise StopIteration来结束迭代

使用yield生成器实现可迭代对象

class PrimeNumber:
    def __init__(self, start, end):
        self.start = start
        self.end = end 

    def isPrimeNum(self, k): 
        if k < 2:
            return False
        for i in xrange(2, k): 
            if k % i == 0:
                return False
        return True
    
    def __iter__(self):
        for k in xrange(self.start, self.end + 1): 
            if self.isPrimeNum(k):
                yield k

for x in PrimeNumber(1, 100):
    print x

注:关注yield的使用

反向迭代(reversed)

class Demo:
    def __init__(self, start, end, step=0.1):
        self.start = start
        self.end = end 
        self.step = step

    def __iter__(self):
        t = self.start
        while t <= self.end:
            yield t
            t += self.step
    
    def __reversed__(self):
        t = self.end
        while t >= self.start:
            yield t
            t -= self.step

a = Demo(1.0, 2.0, 0.1)

for x in reversed(a):
    print x

注:reversed获得的是反向迭代器,iter获得的是正向迭代器

文件切片操作,itertools.islice常用与大文件的切片操作

from itertools import islice

a = range(10)

t = iter(a)

print 'islice start'
for x in islice(t, 2, 4): 
    print x

print 'islice end'
for x in t:
    print x

注:使用islice后的迭代对象会被消耗掉在islice切片的数据,需要重新生成迭代对象

在for中同时迭代多个对象

# 计算每人三课的总分(并行问题,使用zip):
from random import randint
chinese = [randint(60, 100) for _ in xrange(40)]
math = [randint(60, 100) for _ in xrange(40)]
english = [randint(60, 100) for _ in xrange(40)]
total = []
for a, b, c in zip(chinese, math, english):
    total.append(a + b + c)
    
# 统计几个班英语成绩都高于90的(串行问题,使用itertools.chain):
chain([1, 2, 3], [4, 5, 6]) ==> 生成1,2,3,4,5,6的迭代对象

e1 = [randint(60, 100) for _ in xrange(40)]
e2 = [randint(60, 100) for _ in xrange(42)]
e3 = [randint(60, 100) for _ in xrange(39)]
e4 = [randint(60, 100) for _ in xrange(43)]
count = 0
for _ in chain(e1, e2, e3, e4):
    if _ > 90:
        count += 1

拆分含有多个分隔符的字符串

method-1:
def mySplit(s, ds):
    res = [s] 

    for d in ds: 
        t = []
        map(lambda x: t.extend(x.split(d)), res)
        res = t 

    return [_ for _ in res if _]

s = 'ab;cd|efg|hi,,jkl|mn\topq;rst,uvw\txyz'

print mySplit(s, ';,|\t')

==> ['ab', 'cd', 'efg', 'hi', 'jkl', 'mn', 'opq', 'rst', 'uvw', 'xyz']

method-2(推荐使用):
import re
s = 'ab;cd|efg|hi,,jkl|mn\topq;rst,uvw\txyz''
re.split(r'[;,|\t]+', s)

==> ['ab', 'cd', 'efg', 'hi', 'jkl', 'mn', 'opq', 'rst', 'uvw', 'xyz']

注:单个切割直接使用split比较快

判断字符串是否以某个字符串开头或结尾(使用startswith和endswith)

a = 'a.py'
a.startswith('a') ==> True
a.endswith('.py') ==> True

注:startswith和endswith只能接受一个元祖不能使用列表

字符替换(使用re.sub)

import re
a = '2017-11-01'

re.sub(r'(\d{4})-(\d{2})-(\d{2})', r'\2/\3/\1', a)

==> '11/01/2017'

re.sub('(?P\d{4})-(?P\d{2})-(?P\d{2})', r'\g/\g/\g', a)

==> '11/01/2017'

注:使用正则表达式时最好使用'r'防止字符串的转义

拼接字符串(使用str.join)

a = ['a', 'b', 'c', 'd']
'',join(a) ==> 'abcd'
'+'.join(a) ==> 'a+b+c+d'

a = ['abc', 123, 'xyz']
''.join([str(_) for _ in a]) ==> 'abc123xyz'
''.join((str(_) for _ in a)) ==> 'abc123xyz'

注:推荐使用第二种方式,使用生成器方式减少内存占用

对字符串左右居中对齐

method-1:
s = 'abc'
s.ljust(10) ==> 'abc       '
s.rjust(10) ==> '       abc'
s.center(10) ==> '   abc    '
s.ljust(10, '=') ==> 'abc======='

method-2:
s = 'abc'
format(s, '<20') ==> 'abc       '
format(s, '>20') ==> '       abc'
format(s, '^20') ==> '   abc    '

去掉字符串中不需要的字符

method-1(删除首尾,使用strip,lstrip,rstrip):
s = '  abc  123  '
s.strip() ==> 'abc  123'
s.lstrip() ==> 'abc   123  '
s.rstrip() ==> '  abc  123'
s = '--abc--123--'
s.strip() ==> 'abc--123'
s.lstrip() ==> 'abc--123--'
s.rstrip() ==> '--abc--123'
s = '--abc++'
s.strip('-+') ==> 'abc'

method-2(删除中间的字符,使用切片):
s = 'abc+123'
s[:3] + s[4:] ==> 'abc123'

method-3(删除或替换任何位置的字符,使用replace,re.sub):
s = '\tabc\t123\txyz'
s.replace('\t', '') ==> 'abc123xyz'

s = '\tabc\t123\txyz\ropq\r'
import re
re.sub('[\t\r]', '', s) ==> 'abc123xyzopq'

注:replace只能替换一种字符,re.sub可以使用正则表达式替换多种

method-4(删除任何位置的字符,使用translate):
s = 'abc\t123\nref\r'
s.translate(None, '\r\t\n') ==> 'abc123ref'

文件读写

python2:
s = u'你好'
with open('test.txt', 'w') as f:
    f.write(s.encode('utf-8'))
    
with open('test.txt', 'r') as f:
    s = f.read().decode('utf-8')
    
python3:
s = '你好'
    
# 't'指的是一种文本模式
with open('demo.txt', 'wt', encoding='utf-8') as f:
    f.write(s)
    
with open('demo.txt', 'rt', encoding='utf-8') as f:
    s = f.read()

注:
Python2和Python3字符对应关系
python2   python3
str      ==> bytes
unicode   ==> str
一般都会将字节写入物理文件

文件读写缓冲

# 全缓冲
f = open('demo.txt', 'w', buffering=2048)

# 行缓冲
f = open('demo.txt', 'w', buffering=1)
...
# 在写入'\n'之前都是写入缓冲,在写入'\n'后会将缓冲中的内容写入磁盘
f.write('\n')

# 无缓冲
f = open('demo.txt', 'w', buffering=0)
# 执行write即写入磁盘,不做缓冲

注:open默认是全缓冲,大小是4096

文件状态访问

系统调用:os.stat,os.fstat,o.lstat获取文件状态
快捷函数:os.path下的函数,使用起来更加简洁
import os
os.path.isdir 判断是否是文件夹
os.path.islink 判断是否是连接文件
os.path.isfile 判断是否是普通文件
os.path.isabs 判断是否是绝对路径
...

如何使用临时文件(使用tempfile中的TemporaryFile和NamedTemporaryFile)

# 获得临时文件对象
f = TemporaryFile()
# 将数据写入磁盘
f.write('abc' * 4096)
# 先将文件指针指向开头后才能将数据都会到内存中
f.seek(0)
# 根据具体需求量读取数据
f.read(100)

# delete自动清除参数默认是True,如果需要保留可以设置FALSE
ntf = NamedTemporaryFile()
# NamedTemporaryFile创建的临时文件路径
ntf.name

注:在系统文件中找不到TemporaryFile创建的临时文件,使用NamdTemporaryFile创建的临时文件可以在系统中找到TemporaryFile创建的只能在本进程中访问NamedTemporaryFile创建的可以多进程进行访问

csv读写(使用csv模块)

import csv

rf = open('demo.csv', 'rb')

# reader 是一个可迭代对象
reader = csv.reader(rf)

# 打印每一行
for _ in reader:
    # 每一行都是一个列表
    print _

wf = open('demo.csv', 'wb')

writer = csv.writer(wf)

# 写入的数据是一个列表
writer.writerow(list_obj)

# 写入的数据是由多个元组组成的列表
writer.writerows([tuple_obj, tuple_obj...])

注:一定要使用二进制文件

json文件格式的处理(使用json库)

import json
a = {'a': None, 'b': 1}
json.dumps(a) ==> '{"a": null, "b": 1}'
# 可以去除多余的空格,另外也可以自己定制
json.dumps(a, separators=[',', ':']) ==> '{"a":null,"b":1}'

a = '{"a": null, "b": 1}'
json.loads(a) ==> {'a': None, 'b': 1}

with open('demo.json', 'wb') as f:
    json.dump(a, f)

with open('demo.json', 'rb') as f:
    a = json.load(f)

注:json.dump和json.load作用于文件

excel文件处理(使用第三方库xlrd,xlwt)

pip install xlrd xlwt

book = xlrd.open_workbook('demo.xlsx')

# 所有sheet的列表
book.sheets()

# 根据索引获得单个sheet对象
sheet = book.sheet_by_index(0)

# 根据名字获得单个sheet对象
sheet = book.sheet_by_name(name)

如何派生内置不可变类型并修改实例化行为(使用真正的构造函数new

class IntTuple(tuple):
    def __new__(cls, iterable):
        g = (_ for _ in iterable if isinstance(_, int) and _ > 0)
        return super(IntTuple, cls).__new__(cls, g)  

    def __init__(self, iterable):
        print self ==> (1, 6)
        super(IntTuple, self).__init__(iterable)

t = IntTuple([1, -2, 'abc', 6, ['a', 'b'], 0]) 
print t ==> (1, 6)

注:new是类真正的构造函数,先于init被调用,self是由new构造获得

如何为创建大量实例节省内存(使用slots属性来声明实例属性列表)

class Player1(object):
    def __init__(self, uid, name, status=0, level=1):
        self.uid = uid 
        self.name = name
        self.status = status
        self.level = level

class Player2(object):
    __slots__ = ['uid', 'name', 'status', 'kevek']
    def __init__(self, uid, name, status=0, level=1):
        self.uid = uid 
        self.name = name
        self.status = status
        self.level = level
        
p1 = Player1('001', 'p1')
p1.x = 11

p2 = Player2('002', 'p2')
p2.x == 11 ==> 报错,因为没有__dict__属性做数据的动态绑定

注:使用slots关闭默认的动态属性绑定dict,减少内存也可以用来阻值类的数据动态绑定

如何让对象支持上下文管理(使用enterexit

class Demo(object):
    def start(self):
        """
        with打开后执行的方法
        """
        
        # 主要的操作内容,例如输入密码登录等等操作

        
    def __enter__(self):
        """
        with之前的一些准备工作
        必须要有返回值,作用给with调用后as的对象
        """
        
        # 准备工作,例如打开连接
        
        return self
        
    def __exit__(self, exc_type, exc_val, exc_tb):
        """
        必要参数,在没有异常时,以下参数的值都是None
        exc_type:异常类型
        exc_val:异常的值
        exc_tb:错误路径
        """
        
        # 结束工作,例如关闭连接
        
        # 设置返回值为True,如果有异常也不会抛出
        # return True
        
with Demo() as d:
    d.start()

注:enter在with之前调用,exit在with之后调用遇到异常也会执行exit内的方法

如何创建可管理的对象属性(使用property)

class Demo(object):
    def __init__(self):
        self.show = 0

    def getShow(self):
        return self.show

    def setShow(self, num):
        self.show = num

    SHOW = property(getShow, setShow)
    
d = Demo()
print d.SHOW ==> 0
d.SHOW = 2
print d.show ==> 2

注:property的第一个参数是get操作,第二参数是set操作,第三个属性是del操作需要补充@property的使用方法

类对象之间的<,<=,>,>=,==,!=

# method-1(使用__lt__,__le__,__gt__,__eq__,__ne__):

注:一般有两个参数,一个是self,另一个obj(需要比较的对象)

# method-2(使用functools.total_ordering):
from functools import total_ordering

@total_ordering
class Demo(object):
    def __init__(self):
        pass
        
    def __lt__(self, obj):
        return
        
    def __eq__(self, obj):
        return

注:使用functools.total_ordering只需要重写lteq就可以实现全部<,<=,>,>=,==,!=

# method-3(使用抽象基类):
from functools import total_ordering
from abc import ABCMeta, abstractmethod

@total_ordering
class DemoBase(object):
    @abstractmethod
    def foo(self):
        """
        所有继承该类的子类都需要实现这个方法
        """
        pass
        
    def __lt__(self, obj):
        if not isinstance(obj, DemoBase):
            raise TypeError('obj is not DemoBase')
        return self.foo() < obj.foo()
        
    def __eg__(self, obj):
        if not isinstance(obj, DemoBase):
            raise TypeError('obj is not DemoBase')
        return self.foo() == obj.foo()
    
class Demo1(DemoBase):
    def __init__(self):
        pass
    
    def foo(self):
        """
        此方法必须实现
        """
        pass
        
class Demo2(DemoBase):
    def __init__(self):
        pass
        
    def foo(self):
        """
        此方法必须实现
        """
        pass
        
d1 = Demo1()
d2 = Demo2()

# 使用DemoBase做基类就可以实现<,<=,>,>=,==,!=
# 子类就不需要实现__lt__,__eg__等方法
d1 > d2
d2 <= d2
...

如何会用描述符对实例属性做类型检查

class Attr(object):
    def __init__(self, name, type_):
        # type_ 指python内置类型
        self.name = name
        self.type_ = type_
    
    def __get__(self, instance, cls):
        return instance.__dict__[self.name]
    
    def __set__(self, instance, value):
        if not isinstance(value, self.type_):
            raise TypeError('expected an %s' % self.type_)
        return instance.__dict__[self.name] = value
        
    def __delete__(self, instance):
        def instance.__dict__[self.name]
        
class Person(object):
    name = Attr('name', str)
    age = Attr('age', int)
    height = Attr('height', float)

p = Person()
p.name = 'cancan'
prnt p.name ==> 'cancan'
p.name = 11 ==> raise Exception
...

如何在环状数据结构中管理内存

sys.getrefcount(obj) - 1 ==> 查看对象被引用的次数

gc.collect() ==> 在对象定义了析构函数__del__时不能被清除引用

a = Demo()
a_wref = weakref.ref(a)弱引用方式,在循环引用中可以减少引用次数,方便管理

使用字符串来调用类中的方法

# method-1(使用getattr方法):

# 第一个参数是类对象名,第二参数是类对象拥有的方法名字符串,第三个参数是在没有找到对应方法时返回的值
f = getattr(class_obj_name, class_obj_func_name, None)
if f:
    f(args)
    
# method-2(使用operator.methodcaller)
from operater import mehtodcaller

s = 'abc123abc456'

# 从第四个参数找'abc'
s.find('abc', 4)  ==> 6

mehtodcaller('find', 'abc', 4)(s) ==> 6

多线程使用(使用threading.Thread)

from threading import Thread

def foo():
    """
    需要线程处理的函数
    """
    pass

# method-1(直接使用方法):
# 传入args是一个元组,当只有一个参数时需要加','
T = Thread(target=foo, args=(arg1, arg2..))
T.start()

# method-2(使用类):
class Demo(Thread):
    """
    使用类可以更好的封装数据
    """
    
    def __init__(self, args):
        
        # 必须条用Thread这个父类的构造器
        Thread.__init__(self)
        self.args = args
    
    def run(self):
        """
        必须实现该方法
        """
        
        # 指定需要运行的函数
        foo()

threads = []
for _ in xrange(1, 10):

    T = Demo(args)
    threads.append(T)
    # 使用start()会进入run()函数
    T.start()

for _ in threads:
    # join()是一个阻塞函数,run()没有运行完不会退出
    _.join()

注:python中的多线程只适合用于多I/O密集型的环境,如果是CPU密集型,建议使用多进程

线程间事件通知(使用Threading.Event)

from Threading import Event

def f(e):
    print 'f 0'
    e.wait()
    print 'f 1'
    
e = Event()

t = Thread(target=f, args=(e, ))

t.start()

==> 'f 0' # 此时等待e调用set()方法
    
e.set()

==> 'f 1'

# 清理后才可以重复使用
e,clear()

注:
1.两个线程都需要是Event
2.wait是一个阻塞函数,会等待另一个线程中set的调用

如何实现线程本地的数据(使用threading.local)

from threading import local

l = local()

# 创建的x是在调用该线程时的私有数据
# 注意:目前l属于主线程
l.x = 'a'

# 主线程中运行会修改参数
def f();
    l.x = 'b'
f() 
print l.x ==> 'b'

# 子线程中运行不会修改主线程中的数据
l.x = 'a'
threading.Thread(target=f).start()
print l.x ==> 'a'

如何使用线程池(python3中使用concurrent.futures.ThreadPoolExecutor)

# 3表示线程池的个数
executor = ThreadPoolExecutor(3)

def f(a, b):
    print('f', a, b)
    return a ** b

# 使用池中的一个线程
executor.submit(f, 2, 3,) ==> f 2 3

# future 是使用池中线程之后返回的对象
future = executor.submit(f, 2, 3) ==> f 2 4
# 如果f函数运行时间很长,result()就会阻塞
future.result() ==> 16

# 同时调用
executor.map(f, [2, 3, 4], [4, 5, 6])
==> f 2 4
    f 3 5
    f 5 6

注:如果线程数大于线程池的数,超出的线程就会等待线程池中的线程运行完毕,有了空位才会运行

如何使用多进程(使用multiprocessing.Process)

from multiprocessing import Process

def f(s): print s

# 启动一个子进程
p = Process(target=f, args=('hello', ))
p.start() ==> 'hello'

# 等待一个进程结束
p.join()

# 与多线程的不同之处在于使用的虚拟地址空间是不同的
x = 1
def f():
    global x
    x = 5
    
# 在本进程调用这个函数
f()
x ==> 5

# 启动子进程来测试
x = 1
p = Process(target=f).start()
# 在主进程中查看x,x并没有变化,得出子进程和主进程看到的x不是同一个
# 说明个个进程之间他们的虚拟地址是独立的
x ==> 1

进程间进行通信(使用multiprocessing.Queue和multiprocessing.Pipe)

# method-1(使用multiprocessing.Queue):
from multiprocessing import Process, Queue

q = Queue()

def f(q):
    # 等待主进程传一个值
    print 'start'
    print q.get()
    print 'end'

Process(target=f, args=(q, )).start()
==> start
# 打印start,等待主进程传入一个值

q.put(100)
# 当传入一个值后子进程立马执行
==> 100
    end

# method-2(使用multiprocessing.Pipe):
from multiprocessing import Process, Pipe

# Pipe会创建一个双向管道
c1, c2 = Pipe()

# 从c1传入'a'
c1.send('a')

# 只能从c2读取'a'
c2.recv() ==> 'a'

# 从c2传入'a'
c1.send('a')

# 只能从c1读取'a'
c2.recv() ==> 'a'

def f(c):
    c.send(c.recv() * 2)

# 启动子进程,等待c1端输入
Process(target=f, args=(c2,)).start()

c1.send(2)

c1.recv() ==> 4

如何使用装饰器

# -*- coding: utf-8 -*-

def memo(func):
    cache = {}  
    def wrap(*args):
        if args not in cache:
            cache[args] = func(*args)
        return cache[args]
    return wrap

# [题目1] 菲波那切数列(Fibonacci Sequence),又称黄金分割数列
#         指的是这样一个数列: 1,1,2,3,5,8,11.21...
#         这个数列从第三项开始,每一项都等于前面两项之和,求数列第n项

@memo
def fibonacci(n):
    if n <= 1:
        return 1
    return fibonacci(n - 1) + fibonacci(n - 2)

print fibonacci(50)

# [题目2] 一个共有10个台阶的楼梯,从下面走到上面,一次只能迈1~3个台阶
#         并且不能后退,走完这个楼梯共有多少种方法

@ memo
def climb(n, steps):
    count = 0 
    if n == 0:
        count = 1 
    elif n > 0:
        for step in steps:
            count += climb(n - step, steps)
    return count

print climb(10, (1,2,3))

为被装饰的函数保存元数据

from functools import update_wrapper, wrap, WRAPPER_ASSIGNMENTS, WRAPPER_UPDATES

method-1(使用update_wrapper函数):

def Demo(func):
    def wrapper(*args, **kwargs):
        """is wrapper"""
        print 'is wrapper'
        func(*args, **kargs)
    
    # 使用原函数的属性替换包裹函数,倒数第二个参数是替换,倒数第一个参数是合并
    # 后两个参数可以使用如下默认参数
    # WRAPPER_ASSIGNMENTS ==> ('__module__', '__name__', '__doc__')
    # WRAPPER_UPDATES ==> ('__dict__', )
    update_warapper(wrapper, func, ("__name__", "__doc__"), ("__dict__", ))
    return wrapper

method-2(使用wraps装饰器):

def Demo(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        """is wrapper"""
        print 'is wrapper'
        func(*args, **kargs)
    return wrapper

注:
函数元数据
f.__name__ : 函数的名字
f.__doc__ : 函数文档字符串
f.__module__ : 函数所属模块名
f.__dict__ : 属性字典
f.__defaults__ : 默认参数元祖
...

定义带参数的装饰器(使用与python3)

# python3 中的模块
from inspect import signature


def typeAssert(*ty_args, **kwy_args):
    def decorator(func):
        """
        解决思路:
            func -> a,b 
            d = {'a': int, 'b', str}
            使用signature实现上述d的生成
            wrapper通过d来做验证
            arg in d, instache(arg, d[arg])
        """

        # 获得func的签名
        sig = signature(func)
        btypes = sig.bind_partial(*ty_args, **kwy_args).arguments
        
        def wrapper(*args, **kwargs):
             for name, obj in sig.bind(*args, **kwargs).arguments.iteritems():
                if name in btypes:
                    if not isinstance(obj, btypes[name])
                        raise TypeError('%s must be %s' % (name, btypes[name]))
            return func(*args, **kwargs)
        return wrapper
    return decorator

@typeAssert(int, str, list)
def f(a, b, c):
    print(a, b, c)
    
f(1, 'abc', [1, 2, 3])
f(1, 2, [1, 2, 3])
==> 1 abc [1, 2, 3]
    TypeError: 'b' must be ''

如何实现属性可修改的函数装饰器(适用于python3)

from functools import wraps
from random import randint

import time
import logging

def warn(timeout):
    def decorator(func):
        def wrapper(*args, **kwargs):
            start = time.time()
            res = func(*args, **kwargs)
            used = time.time() - start
            if used > timeout:
                msg = '%s : %s > %s' % (func.__name__, used, timeout)
                logging.warn(msg)
            return res 
        def setTimeout(k):
            # python3 特有的类型,用于设置闭包内的全局变量
            # 如果不声明,此函数中的timeout与warn(timeout)中的timeout不是同一个
            nonlocal timeout
            timeout = k 
        wrapper.setTimeout = setTimeout
        return wrapper
    return decorator

@warn(0.5)
def test():
    print  'in test'
    while randint(0, 1): 
        time.sleep(0.5)

for _ in range(30):
    test()

test.setTimeout(1)
for _ in range(30):
    test()

你可能感兴趣的:(Python高级编程)