Python的一些高级特性

Python的一些高级特性

函数相关

关键字参数
1
2
def person(name, age, **kw):
print 'name:', name, 'age:', age, 'other:', kw

示例一:

1
2
3
4
5
person('Michael', 30)
person('Bob', 35, city='Beijing')
person('Adam', 45, gender='M', job='Engineer')

示例二:

1
2
kw = {'city': 'Beijing', 'job': 'Engineer'}
person('Jack', 24, **kw)
高阶函数

一个函数可以接收另一个函数作为参数,这种函数就称之为高阶函数

  • 变量可以指向函数
1
2
3
f = abs
f(-10)
  • 函数名也是变量
1
2
3
4
5
abs = 10
abs
abs(-10)
  • 传入参数
1
2
3
4
f = abs
def add(x,y,f):
return f(x) + f(y)
  • 内建map()

    map()函数接收两个参数,一个是函数,一个是序列,map将传入的函数依次作用到序列的每个元素,并把结果作为新的list返回

1
2
3
4
5
6
7
8
def f(x):
return x * x
map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9])
//'' "" 区别
  • 内建reduce()

    reduce把一个函数作用在一个序列[x1, x2, x3…]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算

示例一:

1
2
3
4
5
6
def add(x,y):
return x + y
reduce(add,range(1,10))
sum(range(1,10))

示例二:

1
2
3
4
def fn(x,y)
return x * 10 + y
reduce(fn,[1,3,5,7,9])
  • 内建filter()

    filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素

1
2
3
4
5
//奇数
def is_odd(n):
return n % 2 == 1
filter(is_odd,range(1,10))
  • 内建sorted()

示例一:

1
2
3
4
5
6
7
8
9
# 逆序
def reversed_cmp(x, y):
if x > y:
return -1
if x < y:
return 1
return 0
sorted([36, 5, 12, 9, 21], reversed_cmp)

示例二:

1
2
3
4
5
6
7
8
9
10
11
# 字符串忽略大小写排序
def cmp_ignore_case(s1, s2):
u1 = s1.upper()
u2 = s2.upper()
if u1 < u2:
return -1
if u1 > u2:
return 1
return 0
sorted(['bob', 'about', 'Zoo', 'Credit'], cmp_ignore_case)
返回函数
1
2
3
4
5
6
7
8
9
10
11
12
13
def lazy_sum(L=[]):
def sum():
ax = 0
for n in L:
ax = ax + n
return ax
return sum
f = lazy_sum(range(1,10))
f
f()
装饰器

在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)

示例一:

1
2
3
4
5
6
7
8
9
def log(func):
def wrapper(*args, **kw):
print 'call %s():' % func.__name__
return func(*args, **kw)
return wrapper
@log
def name():
print 'lly'

@log放到now()函数的定义处,相当于执行了语句:

1
now = log(now)

由于log()是一个decorator,返回一个函数,所以,原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()函数中返回的wrapper()函数。

示例二:

1
2
3
4
5
6
7
8
9
10
11
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print '%s %s():' % (text, func.__name__)
return func(*args, **kw)
return wrapper
return decorator
@log('netease')
def name():
print 'lly'

示例三:

1
2
3
4
5
6
7
8
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print 'call %s():' % func.__name__
return func(*args, **kw)
return wrapper

面向对象

属性、方法的动态绑定和限制

一、属性的动态绑定

1
2
3
4
5
6
7
class Student(object):
pass
s = Student()
s.name = 'lly'
print s.name

二、方法的动态绑定

1
2
3
4
5
6
7
8
9
10
11
12
class Student(object):
pass
def set_age(self, age): # 定义一个函数作为实例方法
self.age = age
from types import MethodType
s.set_age = MethodType(set_age, s, Student)
s.set_age(25)
s.age

三、类的方法绑定

1
2
3
4
5
6
7
8
9
class Student(object):
pass
def set_score(self, score):
self.score = score
from types import MethodType
Student.set_score = MethodType(set_score, None, Student)

四、限制动态添加

Python允许在定义class的时候,定义一个特殊的slots变量,来限制该class能添加的属性

1
2
3
4
5
6
7
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称 插槽
s = Student() # 创建新的实例
s.name = 'Michael' # 绑定属性'name'
s.age = 25 # 绑定属性'age'
s.score = 99 # 绑定属性'score'

slots定义的属性仅对当前类起作用,对继承的子类是不起作用

安全简洁的属性

Python内置的@property装饰器就是负责把一个方法变成属性调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Student(object):
@property
def score(self):
return self._score
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
s = Student()
s.score = 60 # OK,实际转化为s.set_score(60)
s.score # OK,实际转化为s.get_score()

多进程和多线程

多进程

Unix/Linux操作系统提供了一个fork()系统调用,它非常特殊。普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回。

子进程永远返回0,而父进程返回子进程的ID。这样做的理由是,一个父进程可以fork出很多子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程的ID。

示例一:

1
2
3
4
5
6
7
8
import os
print 'Process (%s) start...' % os.getpid()
pid = os.fork()
if pid==0:
print 'I am child process (%s) and my parent is %s.' % (os.getpid(), os.getppid())
else:
print 'I (%s) just created a child process (%s).' % (os.getpid(), pid)

示例二:

1
2
3
4
5
6
7
8
9
10
def run_proc(name):
print 'Run child process %s (%s)...' % (name, os.getpid())
if __name__=='__main__':
print 'Parent process %s.' % os.getpid()
p = multiprocessing.Process(target=run_proc, args=('test',))
print 'Process will start.'
p.start()
p.join()
print 'Process end.'
多线程

Python的标准库提供了两个模块:thread和threading,thread是低级模块,threading是高级模块,对thread进行了封装。绝大多数情况下,我们只需要使用threading这个高级模块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import time, threading
# 新线程执行的代码:
def loop():
print 'thread %s is running...' % threading.current_thread().name
n = 0
while n < 5:
n = n + 1
print 'thread %s >>> %s' % (threading.current_thread().name, n)
time.sleep(1)
print 'thread %s ended.' % threading.current_thread().name
print 'thread %s is running...' % threading.current_thread().name
t = threading.Thread(target=loop, name='LoopThread')
t.start()
t.join()
print 'thread %s ended.' % threading.current_thread().name

线程加锁

1
2
3
4
5
6
7
8
9
10
11
12
13
balance = 0
lock = threading.Lock()
def run_thread(n):
for i in range(100000):
# 先要获取锁:
lock.acquire()
try:
# 放心地改吧:
change_it(n)
finally:
# 改完了一定要释放锁:
lock.release()

因为Python的线程虽然是真正的线程,但解释器执行代码时,有一个GIL锁:Global Interpreter Lock,任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。

简单爬虫

1
2
3
4
5
6
7
import urllib
uf = urllib.urlopen('https://baidu.com')
uf.read()
urllib.urlretrieve('urlpath','filename')