前言
1. virtualenv的使用
virtualenv作用是并行管理多个python程序,解决因多个python版本不兼容的问题
使用方法如下
1)安装pip: apt install pip
2)安装virtualenv:pip install virtualenv
3)建立工作目录:virtualenv test1
4)在工作目录下安装文件:cd test1, source bin/activate, pip install tornado
2. 推荐参考书
1)程序员的数学
2)大话数据结构,大话设计模式(后期再看)
3)C语言
4)python标准库
5) python基础教程
6)官方文档 docs.python.org
一,函数
进阶篇 函数 第一节
1.函数基本概念
注意点:
a: 输出函数时要加小括号
b: 函数中的return语句非常重要
c: 函数体为空的话,这里要写一个pass
def func_name():
pass# 函数体为空的话,这里要写一个pass,如果不写,函数是不成立的,
输出函数时要加小括号
def func1():
return "hello,world"
print func1()
错误例子1
print "a>b" if a>b else pass 执行的时候会报错,其实这个三元表达式是执行2个命令,print 和后面的条件语句,如果a>b 会执行print "a>b", 否则,会执行print pass,但是这个pass是一个命令体中的语句,结果就会报错,最好写成如下
if a>b:
print "a>b"
else:
pass
错误例子2
def func2():
print 123
test = func2()
print type(test)
输出结果是nonetype,所以函数中的return语句非常重要。
2.参数 <=> 抽象
例如
def add(num1,num2):
return num1+num2
print add(1,3)
print add(3,8)
3.参数分为可选参数,必选参数
1)计算不定数量的整数相加
def add(*num): #这里*会把num定义为一个tuple类型,2个*会定义为字典类型
d = 0
for i in num:
d += i
return d
print add(1,2,3,4,5)
print add(1,2,3)
print add(2,4,6,8,1,2,3,4,12312,12314,123,123,123)
2)可选和必选语法上的区别
1.可选参数 是有默认值的
2.必须参数 是没有默认值的
默认值和没有默认值的区别在于 “=”
例如
def add(num1,num2=4)
return num1+num2
print add(1)
如果参数中的"=4"没有的话,调用add函数的时候就需要写2个参数,如果定义函数的时候,2个参数都有等号,那么调用的时候直接用print add()就行
3) 函数的健壮性--考虑到大部分结果,并得到应急反馈处理。
1)各种情况下会返回什么东西(异常处理,条件判断)
2)自定义你想要的返回结果
def add(num1 ,num2):
if isinstance(num1,int) and isinstance(num2, int):
return num1+num2
else:
return ‘参数里有不是数字的类型‘
print add(‘a‘,(1,2,3))
print add(1,2)
测试方法,断言
assert add(1,2) == 3
assert add(2,4) == 3
在这里,如果断言正确,不会输出任何信息,如果断言错误,就会返回AssertionError
二,函数第2节
1.元组,list,字典都能迭代,int和string不能迭代
2. 怎么去学习使用函数
(1)别管那么多复杂的,先直接把功能实现了。
(2)抽象成函数:命名规范,伪代码,参数默认值。
(3)将函数变得更健壮,让它可以跑很多地方
1.假设你写的函数是要交给你的基友用 -》 功能完整
2.假设你写的函数是要交给你的学弟用 -》 异常处理完善
(4) 测试
1.assert
2.对函数的返回进行一个值和类型的测试。
3.单元测试
def func1(a,b,c,d,e):
“”“
@a:
”“”
pass
3. 命名
下划线命名线 get_doc
驼峰命名法 getDocFromUrl
为什么要用默认值:
1.更省事
2.更可配置
4.练习题
三. 函数第3节
1. assert
在开发一个程序时候,与其让它运行时崩溃,不如在它出现错误条件时就崩溃(返回错误)。这时候断言assert就显得非常有用。
assert不能放在程序流程中,它是用于程序调试的
例1. 下面使用assert的方式是不对的
for item in args:
assert isinstance(item,int),‘parameter is integer only‘
return max(args),min(args)
例2
这段代码用来检测数据类型的断言,因为 a_str 是 str 类型,所以认为它是 int 类型肯定会引发错误。
>>> a_str = ‘this is a string‘
>>> type(a_str)
<type ‘str‘>
>>> assert type(a_str)== str
>>> assert type(a_str)== int
Traceback (most recent call last):
File "<pyshell#41>", line 1, in <module>
assert type(a_str)== int
AssertionError
2.自省与函数---func.__code__
例如
def func1(arg1,arg2):
return arg1 == arg2
print dir(func1.__code__)
print func1.__code__.co_varnames #(‘arg1‘, ‘arg2‘),返回函数的参数
print func1.__code__.co_filename #test1.py,返回脚本的文件名。
print help(func1.__code__)
都试一下会输出什么
3.作用域问题再议
例子1
arg = 1
def func1():
arg = 3
func1()
print arg
返回结果是1,说明局部变量只会在函数内部生效。
例子2
arg =1
def func1():
global arg
arg = 3
def func2():
global arg
arg =4
func2()
func1()
print arg
结论:
1) func2()和func1()的顺序不同,输出结果也会不同
2) global关键字可以把局部变量变为全局变量
4.可变参数的魔法与禁忌
例子1
def func1(arg):
arg[0] = 5 用于测试
return arg
print func1([1,2,3])
结果为[5,3,4],参数arg为列表,是可变的参数。
例子2
def func1(arg):
arg[0] = 5 用于测试
return arg
tlist = [1,2,3]
print func1(tlist)
print tlist #引入可变参数会很危险,这里引入的tlist参数自己也被修改了
四. 函数第4节
step1:lambda之再议
1.lambda是一个表达式,它没有名称,存储的也不是代码块,而是表达式。
2.它被用作执行很小的功能,不能在里面使用条件语句。但是可以执行三元表达式,比如例2
3.也可以执行列表推导式,比如例子3
例子1
d = lambda x:x+1
print d(2)
输出为3,等价于函数
def e(x)
return x+1
例子2
>>> d = lambda x:x+1 if x>0 else "error"
>>> print d(3)
4
>>> print d(-1)
error
例子3
>>> g = lambda x:[(x,i) for i in xrange(0,10)]
>>> print g(3)
[(3, 0), (3, 1), (3, 2), (3, 3), (3, 4), (3, 5), (3, 6), (3, 7), (3, 8), (3, 9)]
例子4
>>> t = [1,2,3,4,5]
>>> g = filter(lambda x:x>3,t) #自动把t的值代入x中
>>> print g
[4, 5]
step2:函数参数总结
1.位置匹配 func(name)
def func(arg1,arg2,arg3):
return arg1,arg2,arg3
print func(1,2,3) #按位置传参
2.关键字匹配 func(key=value)
def test(a=‘‘,b=‘None‘,c=‘‘):
return a,b,c
print test(a=2,c=3)
print test(c=9,a=1)
print test(a=3,b=4,c=5)
print test()
输出结果如下
(2, ‘None‘, 3)
(1, ‘None‘, 9) #说明参数顺序变化也没关系
(3, 4, 5)
(‘‘, ‘None‘, ‘‘)
3.收集匹配
如果是在参数中没有定义的位置参数,就会设为元组或者字典;
*kargs 元组
**kw 字典
例子
def func2(a,*kargs,**kw):#比较有位置参数a和没有的区别
return kargs
print func2(2,3,4,5,[1,2,3],{1:2,3:4})
4.参数顺序
参数位置规则:位置匹配参数 > 关键字匹配参数 > 元组参数 > 字典参数; 比如def func2(a,d,b=4,*kargs,**kw)
step3:接触递归
1.递归是调用自身
2.理解下面的函数
"""
def func(i):
if i<100:
return i + func(i+1)
return i
print func(3)
print func(10)
"""
五. 面向对象
初识class
1. class的基本定义
class test(object):
a=1 #a称为test的属性
def func_1(self): #在类中定义的函数被称为方法,方法的第一个参数必须是self。
pass
t = test()
print t.a
print t.func_1()
2. __init__方法-------构造函数,
作用:实例化之前可以先引入一些必要的参数
例子1:定义一个空方法
def __init__(self):
pass
例子2:
class Person: #类名后面不加参数也行
def __init__(self,name,age):
self.name=‘‘
self.age=0
p=person(‘tom‘,20)
print p
这样的输出结果是
hong@hong-VirtualBox:~$ python test2.py
<__main__.person instance at 0x7f5f8104c560>
例子3:将对象的内容打印出来
class test(object): #所有的class都是object的派生类
def __init__(self,var1): #注意这里的逗号
self.var1 = var1 #把参数var1赋值给self.var1,这样self.var1就能在类中进行全局调用
def get(self,a=None): #把参数a去掉和a=None是等效的,这样就不用引入参数了
return self.var1 #全局调用self.var1,一般的函数是不能使用在其他函数中的变量的
t = test("hello,my name is hong")
print t.get()
例子4:用于生成对象的字符串表示的方法__str__
class test:
def __init__(self,var1):
self.var1 = var1
def get(self,a=None):
return self.var1
def __str__(self):
return self.var1
t = test("hello, my name is hong")
print t.get()
print type(t.get())
print str(t)
print type(str(t))
输出如下
hello, my name is hong
<type ‘str‘>
hello, my name is hong
<type ‘str‘>
注意:这里get方法和__str__方法是等效的。
例子5:把上例改成2个参数
class test:
def __init__(self,var1,var2):
self.var1 = var1
self.var2 = var2
def get(self):
return self.var1,self.var2
def __str__(self):
return self.var1,self.var2
t = test("hello",33)
print t.get()
print str(t)
这样写会返回一个错误
hong@hong-VirtualBox:~$ python test2.py
(‘hello‘, 33)
Traceback (most recent call last):
File "test2.py", line 13, in <module>
print str(t)
TypeError: __str__ returned non-string (type tuple)
改成如下代码就对了,自己琢磨一下
class test:
def __init__(self,var1,var2):
self.var1 = var1
self.var2 = var2
def get(self):
return self.var1,self.var2
def __str__(self):
return "(%s,%d)" % (self.var1,self.var2)
t = test("hello",33)
print t.get()
print type(t.get())
print str(t)
print type(str(t))
输出结果如下
hong@hong-VirtualBox:~$ python test2.py
(‘hello‘, 33)
<type ‘tuple‘>
(hello,33)
<type ‘str‘>
3. 析构函数,是用作销毁的,这种方法不常用,因为class被销毁时,python有内部机制会自动销毁里面的数据。
def __del__(self):
del self.arg1
del self.arg2
t = test(1,4)
print t.a
print t.func_1()
4. class和函数的区别
例子1:一个最基本的对象
class test(object):#如果是空类,可以继承object类
def get(self): #类里面定义函数,其中的参数self代表的是对象本身,可以在class内部全局调用
return "hello"
对比一下,定义一个基本函数
def get():
return "hello"
t = test() #t是test的一个实例
print t.get() #get称为test对象的专属方法,不能被其他函数调用,这个就是使用对象的内置方法
print get() #自定义的函数和对象的内置方法进行对比
例2. 在get()函数中增加一个参数a
class test(object):
def get(self,a): #引入一个参数a,
return a
def got(a): #自定义的函数也引入一个参数a
return a
t = test()
new_var = 4
print t.get(new_var)
print got(new_var)
输出都是4
5. 私有变量
再person中,在变量名age开头加上2个下划线,表明age是私有变量,这样age只能person类中访问;不以下划线打头的变量是公有变量,任何代码都可访问他们。
在编写大型程序时,一条实用的经验规则是,首先将所有对象变量都设置为私有的(即以2个下划线打头),再在有充分理由的情况下将其改为公有的,可以避免无意间修改对象内部变量导致的错误。
6. __repr__和__str__的区别
class Test(object):
def __init__(self, value=‘hello, world!‘):
self.data = value #这里说明并非一定写成self.value=value
>>> t = Test()
>>> t
<__main__.Test at 0x7fa91c307190>
>>> print t
<__main__.Test object at 0x7fa91c307190>
我测试的是t和print t,效果是一样的。
# 看到了么?上面打印类对象并不是很友好,显示的是对象的内存地址# 下面我们重构下该类的__repr__以及__str__,看看它们俩有啥区别
1) 重构__repr__class TestRepr(Test):
def __repr__(self):
return ‘TestRepr(%s)‘ % self.data
>>> tr = TestRepr()
>>> tr
TestRepr(hello, world!)
>>> print tr
TestRepr(hello, world!)
# 重构__repr__方法后,不管直接输出对象还是通过print打印的信息都按我们__repr__方法中定义的格式进行显示了
2) 重构__str__
calss TestStr(Test):
def __str__(self):
return ‘[Value: %s]‘ % self.data
>>> ts = TestStr()
>>> ts
<__main__.TestStr at 0x7fa91c314e50>
>>> print ts
[Value: hello, world!]
# 你会发现,直接输出对象ts时并没有按我们__str__方法中定义的格式进行输出,而用print输出的信息却改变了
总结:
__repr__和__str__这两个方法都是用于显示的,__str__是面向用户的,而__repr__面向程序员。
- 打印操作会首先尝试__str__和str内置函数(print运行的内部等价形式),它通常应该返回一个友好的显示。
- __repr__用于所有其他的环境中:用于交互模式下提示回应以及repr函数,如果没有使用__str__,会使用print和str。它通常应该返回一个编码字符串,可以用来重新创建对象,或者给开发者详细的显示。
当我们想所有环境下都统一显示的话,可以重构__repr__方法;当我们想在不同环境下支持不同的显示,例如终端用户显示使用__str__,而程序员在开发期间则使用底层的__repr__来显示,实际上__str__只是覆盖了__repr__以得到更友好的用户显示
7. 装饰器
1. @property,这个东西可以直接把函数当做属性来用,例如
class test(object):
@property
def d(self):
return 4
t=test()
print t.d #使用了装饰器,这里可以直接写t.d,就可以输出值,而不是t.d()
2. @staticmethod装饰器,可以不把类test实例化,就能使用其中的方法,如下
class test(object):
@staticmethod #静态方法,把命名空间放在了类test中
def d(): #这里不需要加self参数了,类的普通内置方法的时候才会加self参数,这里和在类外面定义的函数是一样的
return 4
print test.d() #不用实例化,直接用class的名称来执行。
8. 继承
例子1
class Base(object):
def __init__(self,name)
self.name = name
class b(Base): #相当于在b中也有一个init函数
def get_name(self):
return self.name #如果写name就是错误的
new_class = b("lilei") #把b实例化
print new_class.get_name()
六. 模块
1.模块的基本概念
模块其实就是一个py文件,比如python内置模块linecache,可以用dir(linecache)查看模块内置方法
2.导入模块的方法
1)import #导入全部方法
import linecache
>>> dir(linecache)
[‘__all__‘, ‘__builtins__‘, ‘__doc__‘, ‘__file__‘, ‘__name__‘, ‘__package__‘, ‘cache‘, ‘checkcache‘, ‘clearcache‘, ‘getline‘, ‘getlines‘, ‘os‘, ‘sys‘, ‘updatecache‘]
>>> linecache.__file__ #linecache的脚本文件位置
‘/usr/lib/python2.7/linecache.pyc‘
2)from module import sth,只导入模块中的某个方法
from linecache import getlines,这样就可以直接使用getlines方法
>>getlines
3)from module import all 导入全部方法
from linecache import *, 这种方式不会导入以下划线 (_) 开头的名称。
注意点,
1)在linecache的脚本文件中,有__all__ =["getline","clearcache","checkcache"],这样用from linecache import *命令导入时,只会导入getline,clearcache,checkcache这三个方法,这三个方法相当于公有方法,所有人都可以使用;其他的方法相当于linecache的私有方法,比如updatecache,其他人不能任意用,使用import linecache时,就不会有这个限制。
2)当用这个方法导入多个模块的时候,不同模块中的方法名字可能会冲突,所以要慎用!
3.自定义模块
例如自定义个模块 m1
#coding=utf-8
def hash():
return 4
那么在其他脚本中就可以使用m1
#coding=utf-8
import m1
print m1.hash()
4.包的创建
包是一群模块的组合,所以先建一个文件夹,比如m2,作为包
1)写初始化文件 __init__.py #此时为空
为了让 Python 将目录当做包,目录下必须包含 __init__.py 文件;这样做是为了防止一个具有常见名字(例如 string)的目录无意中隐藏目录搜索路径中正确的模块。最简单的情况下,__init__.py 可以只是一个空的文件,但它也可以为包执行初始化代码或设置__all__
2)定义一个url.py
#coding=utf-8
def get_page():
return "some page content"
3)在其他脚本中引用包
注意使用from package import item时,item 可以是包的子模块(或子包),也可以是包中定义的一些其它的名称,比如函数、 类或者变量。import语句首先测试 item 在包中是否有定义;如果没有,它假定它是一个模块,并尝试加载它。如果未能找到,则引发ImportError异常。
相反,使用类似 import item.subitem.subsubitem 这样的语法时,除了最后一项其它每项必须是一个包;最后一项可以是一个模块或一个包,但不能是在前一个项目中定义的类、函数或变量
#coding=utf-8
import m2
print dir(m2)#查看都什么内置模块
print m2.__file__ #会导入init文件
#print m2.url是不会调用url.py文件的,执行脚本的时候会出错,那么怎么调用呢?
方法1 -----在python命令行,以及在linux脚本中都能执行
import m2.url
print m2.url
#如果觉得m2.url比较麻烦,可以用import m2.url as url改名,as相当于一个赋值操作,代码如下
import m2.url as url
print url.get_page()
执行结果:
输出getpage()定义的文本信息:some page content
方法2 ---这种方法可以在python命令行中执行,但是在linux的脚本中不能执行。
from m2 import url
print url.get_page()
如果只想使用url的get_page方法,怎么办呢?
方法1
from m2.url import get_page
print get_page()
方法2
from m2 import *
print url.get_page()
那么此时就需要在__init__.py中进行定义__all__ = ["url"],但是经验证,不用在__inti__.py中定义也行。
5. 搜索模块
如果在上面的包中再写一个模块,比如test2.py, 这个在包里的模块怎么引入外部的模块呢? 比如外部模块为m1.py
import sys
sys.path.append("/tmp/m") #添加模块搜索路径,m文件夹中包含着m1.py文件,其实就是定位m1的位置。
import m1
print m1.bash() #调用m1的bash()方法,视频中多了代码 __all__ = [‘hash‘],测试下不会影响吗?经验证,不会影响。
6. 常用模块
1.我们去哪里找模块
pypi.python.org ---- 这个网站有大量的python的模块库
docs.python.org/2.7/ ---- python文档,其中Library Reference包含所有的内置模块的说明
2.我们应该首先选择哪些的模块
首先考虑的是内置模块, 相关文档:http://docs.python.org/2.7/
3.常用模块
3.1 urllib,urllib2 --- 网络方面的模块
import urllib #urllib可能是一个包,包和模块的用法是一样的
dir(urllib)
help(urllib)里面FILE有脚本文件的位置信息
使用方法,比如获取网页内容
d = urllib.urlopen("http://www.baidu.com";)
print d.read()
3.2 datetime, time --- 时间模块
import time
time.time() #得到一个时间戳
import datetime
使用help(datetime),看MODULE DOCS中的网站可以看到非常详细的例子
3.3 os --- 系统模块
import os
3.4 pickle --- 对象序列化
常用数据交换格式 还有json, xml
import pickle
例子
class test(object):
def a(self):
return 4
def b(self):
return 5
d = test() #想把对象做持久化保存,比如放在文件里,那么需要把对象转变为字符串,就需要用到pickle
g=pickle.dumps(d) #对象转为字符串
type(g) #这里就转变成字符串了
g=pickle.loads(g) #反序列化,字符串转为对象
3.5 bsddb --- 一个轻量级的数据库,支持key=>value的字典形式
3.6 logging --- 日志
掌握 info, warning, error,重点看一下
七,异常
exception,中译异常,保守派的圣杯,被滥用的良药。
1. 出错的东西们,他们出了什么错,他们出错 = 被抛出了异常
2. 我们不想让他们出错,继续执行下面的程序,该怎么办?exception来了。
coding=utf-8
a = [1,2,3,4,5,6]
print a[5]
try:
print a[6]
except: #捕获异常
print u"哈哈哈哈,这里出错啦" #出错以后该做什么,这里输出出错信息
print ‘继续往下跑哦‘
3. 基本语法
try:
" 框住了你感觉会抛出异常的代码 "
print "41223123"
print a[6] #这里抛出异常后就会跳到except语句,不会执行下面的print语句。
print "hahaha"
except:
" try代码块里的代码如果抛出异常了,该执行什么内容"
print u"哈哈"
else:
"try代码块里的代码如果没有抛出异常,就执行这里"
print "hoho"
finally:
"不管如何,finally里的代码,是总会执行的"
print "xixi"
4. 异常的应用
import urllib
#异常输出如下
sth_url = "http://wasdasdasd";
try:
d = urllib.urlopen(sth_url)
except:
print "哈哈哈出错了"
else:
content = d.read()
finally:
d.close()
5.我们为什么不让他出错?
其实在开发阶段,我们是可以让任何东西出错的,这样能更快的排错来完善代码
6.什么时候用,怎么用?
我们什么时候用异常? 答:不得不用的时候。
异常怎么用?
1. (我们知道会有哪些问题,分析问题,得到这些问题会抛出的指定异常)捕获不同的异常情况,最好不要只写except,这样捕获所有异常比较笼统。
比如上面的例子,可以通过写多个except语句,完善如下
sth_url = "http://wasdasdasd";
try:
d = urllib.urlopen(sth_url)
except IOError: #打不开网页的异常,这里的IOError是网页不对时python输出的异常信息。
print "网页出错了"
except 语法错误异常:
print "语法错误"
else:
content = d.read()
finally:
d.close()
2.异常的处理,要合理。要有日志。