学习了跟老齐学python,一些基础的知识是掌握了,接下来继续深入学习,廖雪峰的python教程中有一些相对高级一点的内容,包括面向对象编程、进程和线程、电子邮件、web开发等,在现有的基础上学习这些知识,我觉得刚刚好。
day1 函数式编程——2016.9.5
收集参数 * and **
当函数的参数个数不确定时,其它参数全部通过*arg
,以元组的形式由arg
收集起来,例子如下:
def func(x, *arg):
print x #Python 3请自动修改为print()的格式,下同,从略。
result = x
print arg #输出通过*arg方式得到的值
for i in arg:
result +=i
return result
print func(1, 2, 3, 4, 5, 6, 7, 8, 9) #赋给函数的参数个数不仅仅是2个
运行结果:
1 #这是函数体内的第一个print,参数x得到的值是1
(2, 3, 4, 5, 6, 7, 8, 9) #这是函数内的第二个print,参数arg得到的是一个元组
45 #最后的计算结果
上面的例子中:
-
值1传给了x
-
值2,3,4,5,6,7,8,9被塞入一个元组里面,传给了
arg
除了用*args这种形式的参数接收多个值之外,还可以用**kargs的形式接收数值,例如:
>>> def foo(**kargs):
... print kargs #Python 3: print(kargs)
...
>>> foo(a=1,b=2,c=3) #注意观察这次赋值的方式和打印的结果
{'a': 1, 'c': 3, 'b': 2}
如果用**kargs的形式收集值,会得到dict类型的数据,但是,需要在传值的时候说明“键”和“值”,因为在字典中是以键值对形式出现的。
综合以上方法,对于不确定参数个数的函数:
>>> def foo(x,y,z,*args,**kargs):
... print x #Python 3用户请修改为print()格式,下同
... print y
... print z
... print args
... print kargs
...
#结果
>>> foo(1,2,3,4,5,name="qiwsir")
1
2
3
(4, 5)
{'name': 'qiwsir'}
返回函数
对于通常的函数,都会返回结果,例如:
def calc_sum(*args):
s = 0
for x in args:
s +=x
return s
calc_sum(1,2,3,4,5)
Out[17]: 15
但是,如果不需要立刻求和,而是在后面的代码中,根据需要再计算怎么办?可以不返回求和的结果,而是返回求和的函数!
def lazy_sum(*args):
def sum():
s = 0
for x in args:
s += x
return s
return sum
f = lazy_sum(1,2,3,4,5)
f
Out[20]: <function __main__.sum>
f()
Out[21]: 15
闭包
一个需要注意的问题是,返回的函数并没有立刻执行,而是直到调用了f()
才执行。我们来看一个例子:
def count():
fs = []
for i in range(1,4):
def f():
return i*i
fs.append(f)
return fs
f1,f2,f3 = count()
f1
Out[25]: <function __main__.f>
f1(),f2(),f3()
Out[27]: (9, 9, 9)
你可能认为调用f1()
,f2()
和f3()
结果应该是1
,4
,9
,但实际结果都是9
,原因就在于返回的函数引用了变量i
,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i
已经变成了3
,因此最终结果为9
。
如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:
def count():
fs = []
for i in range(1,4):
def f(j):
def g():
return j*j
return g
fs.append(f(i))
return fs
f1,f2,f3 = count()
f1()
Out[30]: 1
f2()
Out[31]: 4
f3()
Out[32]: 9
装饰器
假设我们要增强一个函数例如now()
函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改now()
函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
本质上,decorator就是一个返回函数的高阶函数。所以,我们要定义一个能打印日志的decorator,可以定义如下:
def log(func):
def wrapper(*args, **kw):
print 'call %s():' % func.__name__
return func(*args, **kw)
return wrapper
观察上面的log
,因为它是一个decorator,所以接受一个函数作为参数,并返回一个函数。我们要借助Python的@
语法,把decorator置于函数的定义处:
@log
def now():
print '2016-9-5'
调用now()
函数,不仅会运行now()
函数本身,还会在运行now()
函数前打印一行日志:
>>> now()
call now():
2016-9-5
偏函数
Python的functools模块提供了很多有用的功能,其中一个就是偏函数(Partial function)。functools.partial
就是帮助我们创建一个偏函数的,简单总结functools.partial
的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。
>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
>>> int2('1010101')
85
面向对象编程
类和实例
面向对象的设计思想是从自然界中来的,因为在自然界中,类(Class)和实例(Instance)的概念是很自然的。Class是一种抽象概念,比如我们定义的Class——Student,是指学生这个概念,而实例(Instance)则是一个个具体的Student,比如,Bart Simpson和Lisa Simpson是两个具体的Student
所以,面向对象的设计思想是抽象出Class,根据Class创建Instance。
class Student(object):
def __init__(self,name,score):
self.name = name
self.score = score
def print_score(self):
print '%s: %s' %(self.name,self.score)
def get_grade(self):
if self.score >= 90:
return 'A'
elif self.score >=60:
return 'B'
else:
return 'C'
bart = Student('bart simpson',59)
bart.get_grade
Out[116]: <bound method Student.get_grade of <__main__.Student object at 0x0000000003CDB908>>
bart.get_grade()
Out[117]: 'C'
访问限制
从前面Student类的定义来看,外部代码还是可以自由地修改一个实例的name、score属性:
>>> bart = Student('Bart Simpson', 98)
>>> bart.score
98
>>> bart.score = 59
>>> bart.score
59
如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__
,在Python中,实例的变量名如果以__
开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问,所以,我们把Student类改一改:
class Student(object):
def __init__(self, name, score):
self.__name = name
self.__score = score
def print_score(self):
print '%s: %s' % (self.__name, self.__score)
>>> bart = Student('Bart Simpson', 98)
>>> bart.__name
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute '__name'
外部无法访问实例变量.__name
和实例变量.__score
,但是如果外部代码要获取name和score怎么办?可以给Student类增加get_name
和get_score
这样的方法:
class Student(object):
def __init__(self, name, score):
self.__name = name
self.__score = score
def print_score(self):
print '%s: %s' % (self.__name, self.__score)
def get_name(self):
return self.__name
def get_score(self):
return self.__score
如果又要允许外部代码修改score怎么办?可以给Student类增加set_score方法(可以对参数做检查,避免传入无效的参数:):
class Student(object):
...
def set_score(self, score):
if 0 <= score <= 100:
self.__score = score
else:
raise ValueError('bad score')
继承和多态
面向对象高级编程
使用__slots__
动态绑定允许我们在程序运行的过程中动态给class加上功能,但是,如果我们想要限制class的属性怎么办?比如,只允许对Student实例添加name和age属性。
为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__
变量,来限制该class能添加的属性:
>>> class Student(object):
... __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
...
试一下:
>>> s = Student() # 创建新的实例
>>> s.name = 'Michael' # 绑定属性'name'
>>> s.age = 25 # 绑定属性'age'
>>> s.score = 99 # 绑定属性'score'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'score'
由于'score'
没有被放到__slots__
中,所以不能绑定score
属性,试图绑定score
将得到AttributeError
的错误。
使用@property
Python内置的@property装饰器是负责把一个方法变成属性调用的,它可以让Python做到既能检查参数,又可以用类似属性这样简单的方式来访问类的变量:
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
把一个getter
方法变成属性,只需要加上@property
就可以了,此时,@property
本身又创建了另一个装饰器@score.setter
,负责把一个setter
方法变成属性赋值,于是,我们就拥有一个可控的属性操作:
>>> s = Student()
>>> s.score = 60 # OK,实际转化为s.set_score(60)
>>> s.score # OK,实际转化为s.get_score()
60
>>> s.score = 9999
Traceback (most recent call last):
...
ValueError: score must between 0 ~ 100!
还可以定义只读属性,只定义getter
方法,不定义setter
方法就是一个只读属性:
class Student(object):
@property
def birth(self):
return self._birth
@birth.setter
def birth(self,value):
self._birth = value
@property
def age(self):
return 2016 - self._birth
s.birth = 1991
s.birth
Out[156]: 1991
s.age
Out[157]: 25
s.age = 30
AttributeErrorTraceback (most recent call last)
<ipython-input-158-de6598fd2649> in <module>()
----> 1 s.age = 30
AttributeError: can't set attribute
上面的birth
是可读写属性,而age
就是一个只读属性,因为age
可以根据birth
和当前时间计算出来。
多重继承
定制类
前面已经见到过__slots__
和__len__
这样的特殊函数,前者是为了限制类的属性,后者允许class作用于len()函数,
除此之外,Python的class中还有许多这样有特殊用途的函数,可以帮助我们定制类。
__str__
class Student(object):
def __init__(self,name):
self.name = name
def __str__(self):
return 'Student object (name: %s)' %self.name
s = Student('michael')
s
Out[20]: <__main__.Student at 0x3fa1780>
print s
Student object (name: michael)
__str__
让打印出来的实例,不但好看,而且容易看出实例内部重要的数据。
class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return 'Student object (name=%s)' % self.name
__repr__ = __str__
s = Student('michale')
s
Out[25]: Student object (name: michale)
__iter__
__getattr__
正常情况下,当我们调用类的方法或属性时,如果不存在,就会报错,为了避免这个错误,可以使用__getattr__
方法,该方法动态返回一个属性
class Student(object):
def __init__(self):
self.name = 'Michael'
def __getattr__(self, attr):
if attr=='score':
return 99
>>> s = Student()
>>> s.name
'Michael'
>>> s.score
99
class Chain(object):
def __init__(self,path = ''):
self._path = path
def __getattr__(self,path):
return Chain('%s/%s' %(self._path,path))
def __str__(self):
return self._path
__repr__ = __str__
Chain().status.uer.timeline.list
Out[97]: /status/uer/timeline/list
使用元类
type()
type()
函数既可以返回一个对象的类型,又可以创建出新的类型,比如,我们可以通过type()
函数创建出Hello
类,而无需通过class Hello(object)...
的定义:
def fn(self,name = 'world'):
print 'hello, %s' %name
Hello = type('Hello',(object,),dict(hello = fn))
h = Hello()
h.hello()
hello, world
print type(Hello)
<type 'type'>
print type(h)
<class '__main__.Hello'>
metaclass
metaclass,直译为元类,简单的解释就是:
当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。
但是如果我们想创建出类呢?那就必须根据metaclass创建出类,所以:先定义metaclass,然后创建类。 连接起来就是:先定义metaclass,就可以创建类,最后创建实例。
所以,metaclass允许你创建类或者修改类。换句话说,你可以把类看成是metaclass创建出来的“实例”。
我们先看一个简单的例子,这个metaclass可以给我们自定义的MyList增加一个add方法:
定义ListMetaclass,按照默认习惯,metaclass的类名总是以Metaclass结尾,以便清楚地表示这是一个metaclass:
# metaclass是创建类,所以必须从`type`类型派生:
class ListMetaclass(type):
def __new__(cls, name, bases, attrs):
attrs['add'] = lambda self, value: self.append(value)
return type.__new__(cls, name, bases, attrs)
class MyList(list):
__metaclass__ = ListMetaclass # 指示使用ListMetaclass来定制类
当我们写下__metaclass__ = ListMetaclass
语句时,魔术就生效了,它指示Python解释器在创建MyList
时,要通过ListMetaclass.__new__()
来创建,在此,我们可以修改类的定义,比如,加上新的方法,然后,返回修改后的定义。
错误、调试和测试
进程和线程
进程:对于操作系统来说,一个任务就是一个进程(Process),比如打开一个浏览器就是启动一个浏览器进程,打开一个记事本就启动了一个记事本进程,打开两个记事本就启动了两个记事本进程,打开一个Word就启动了一个Word进程。
线程:有些进程还不止同时干一件事,比如Word,它可以同时进行打字、拼写检查、打印等事情。在一个进程内部,要同时干多件事,就需要同时运行多个“子任务”,我们把进程内的这些“子任务”称为线程(Thread)。
多进程
#常用内建函数
collections
namedtuple
如果想要表示一个坐标,可以使用namedtuple
,
>>> from collections import namedtuple
>>> Point = namedtuple('Point', ['x', 'y'])
>>> p = Point(1, 2)
>>> p.x
1
>>> p.y
2
namedtuple
是一个函数,它用来创建一个自定义的tuple
对象,并且规定了tuple
元素的个数,并可以用属性而不是索引来引用tuple
的某个元素。
deque
deque
是为了高效实现插入和删除操作的双向列表,适合用于队列和栈:
>>> from collections import deque
>>> q = deque(['a', 'b', 'c'])
>>> q.append('x')
>>> q.appendleft('y')
>>> q
deque(['y', 'a', 'b', 'c', 'x'])
OrderedDict
使用dict
时,Key
是无序的。在对dict
做迭代时,我们无法确定Key
的顺序。如果要保持Key
的顺序,可以用OrderedDict
:
>>> from collections import OrderedDict
>>> d = dict([('a', 1), ('b', 2), ('c', 3)])
>>> d # dict的Key是无序的
{'a': 1, 'c': 3, 'b': 2}
>>> od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
>>> od # OrderedDict的Key是有序的
OrderedDict([('a', 1), ('b', 2), ('c', 3)])
Counter
Counter
是一个简单的计数器,例如,统计字符出现的个数:
>>> from collections import Counter
>>> c = Counter()
>>> for ch in 'programming':
... c[ch] = c[ch] + 1
...
>>> c
Counter({'g': 2, 'm': 2, 'r': 2, 'a': 1, 'i': 1, 'o': 1, 'n': 1, 'p': 1})
base64
Base64是一种用64个字符来表示任意二进制数据的方法。