1. 可以通过 print(os.sys.path) 来查看python可导入的包的路径情况,这会打印出一个list。当我们import某一个包时,python会根据这个list,从前向后搜寻相匹配的包来导入。当然我们也可以通过修改这个list来改变python要导入的包,既可以append也可以修改某一维的值。
2. 我们也可以通过在site-packages路径下添加.pth文件来永久添加python环境变量: 在python安装路径下的site-packges路径下新建文件**.pth,然后输入路径,一行一个路径。
python基础
1. 数据类型和变量
- 空格和tab不能混用,一般使用四个空格缩进,可设置编辑器,将tab转换为四个空格。
- Python允许用
r' '
表示' '
内部的字符串默认不转义。 - 如果字符串内部有很多换行,用
\n
写在一行里不好阅读,为了简化,Python允许用'''...'''
的格式表示多行内容。
>>> print('''line1
... line2
... line3''')
line1
line2
line3
- 空值是Python里一个特殊的值,用
None
表示。None
不能理解为0
,因为0
是有意义的,而None
是一个特殊的空值。 - 在Python中,同一个变量可以被反复赋值,而且可以是不同类型的变量,这种变量本身类型不固定的语言称之为动态语言,与之对应的是静态语言。静态语言在定义变量时必须指定变量类型,如果赋值的时候类型不匹配,就会报错。
- 一般用全部大写的变量名表示一个常量, 如:PI = 3.14,但实际上PI还是一个变量。
- 两种除法:‘ / ’,结果永远是浮点数,即使恰好整除时也是如此;‘ // ’,地板除,结果永远是整数。
- Python的整数没有大小限制。Python的浮点数也没有大小限制,但是超出一定范围就直接表示为
inf
(无限大)。
2. 字符串和编码
- 编码问题:在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。
3. list和tuple
- 元组通常由不同的数据构成,而列表是相同类型数据的队列。元组表示的是结构,列表表示的是顺序。比如:我们想要记录一个人的信息(身高体重性别)要用tuple,而要记录一群人的信息要用list。
4. 条件判断
- 要特别注意,不要滥用
break
和continue
语句。break
和continue
会造成代码执行逻辑分叉过多,容易出错。大多数循环并不需要用到break
和continue
语句,我们可以通过改写循环条件或者修改循环逻辑来去掉break
和continue
语句。
函数
1. 函数名其实就是指向一个函数对象的引用,完全可以把函数名赋给一个变量,相当于给这个函数起了一个“别名”。
2. Python的函数返回多值其实就是返回一个tuple。
3. Python中的函数参数
- 位置参数:最常用,按照位置顺序读取。
- 默认参数:为参数设置默认值,简化函数调用。但是要注意:必选参数在前,默认参数在后;当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面。变化小的参数就可以作为默认参数;有多个默认参数时,调用的时候,既可以按顺序提供默认参数,也可以指出默认参数名;默认参数必须指向不变对象;我们在编写程序时,如果可以设计一个不变对象,那就尽量设计成不变对象。
- 可变参数:可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple;定义可变参数和定义一个list或tuple参数相比,仅仅在参数前面加了一个
*
号;Python允许在list或tuple前面加一个*
号,把list或tuple的元素变成可变参数传进去;使用*args表示可变参数是Python的习惯写法。 - 关键字参数:关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict;定义参数时在参数前加“**”,当把一个dict作为关键字参数传入时,把dict的名字前加上“**”传入;使用 **kw表示关键字参数是Python的习惯写法。
- 命名关键字参数:对于关键字参数,函数的调用者可以传入任意不受限制的关键字参数,但是到底传入了哪些,需要在函数内部进行检查;如果要限制关键字参数的名字,就可以用命名关键字参数,指定只接受某些名称的关键字参数;命名关键字参数需要一个特殊分隔符*,*后面的参数被视为命名关键字参数;如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*了;命名关键字参数必须传入参数名,如果没有传入将报错,也就是定义的命名关键字参数都要传入,一个也不能少。
- 参数定义顺序:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。虽然可以组合多达5种参数,但不要同时使用太多的组合,否则函数接口的可理解性很差。
4. 递归函数
- 解决递归调用栈溢出的方法是通过尾递归优化,事实上尾递归和循环的效果是一样的,所以,把循环看成是一种特殊的尾递归函数也是可以的。
- 尾递归是指,在函数返回的时候,调用自身本身,并且return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只用到一个栈帧,而不会出现栈溢出的情况。下例为用尾递归方式求取阶乘:
def fact(n):
return fact_iter(n, 1)
def fact_iter(num, product):
if num == 1:
return product
return fact_iter(num - 1, num * product)
可以看到,return fact_iter(num - 1, num * product)
仅返回递归函数本身,num - 1
和num * product
在函数调用前就会被计算,不影响函数调用。
- 遗憾的是,大多数编程语言没有针对尾递归做优化,Python解释器也没有做优化,所以,即使把上面的
fact(n)
函数改成尾递归方式,也会导致栈溢出。尾递归事实上和循环是等价的,没有循环语句的编程语言只能通过尾递归实现循环。
高级特性
1. 切片
- 对于经常取指定索引范围的操作,用循环十分繁琐,因此Python提供切片(Slice)操作符来简化这种操作。
- L是一个list,L[0:i] 表示从索引0开始取,一直到索引i-1(注意:不是到i),一共i个元素。如果第一个索引是0可以把0省略:L[:i]。L[i:j]取出索引i到j-1。L[i:]取出从索引为i的元素开始到最后一个元素。
- L[-i]取出list中倒数第i个元素;L[-i:] 取出从倒数第i个元素开始一直到最后一个元素;L[-i,-j]取出从倒数第i个元素开始一直到倒数第j+1个元素(不包括倒数第j个元素);L[0:-i]取出从第一个元素开始到倒数第i+1个元素,不包括倒数第i个。记住倒数第一个元素的索引是-1。
- L[:]取出所有数;L[::k]取出所有数,每隔k个取一个;L[:n:k]取前n个数,每隔k个取一个。
- 字符串
'xxx'
也可以看成是一种list,每个元素就是一个字符。因此,字符串也可以用切片操作,只是操作结果仍是字符串。在很多编程语言中,针对字符串提供了很多各种截取函数(例如,substring),其实目的就是对字符串切片。Python没有针对字符串的截取函数,只需要切片一个操作就可以完成,非常简单。
2. 迭代
- 如果给定一个list或tuple,我们可以通过
for
循环来遍历这个list或tuple,这种遍历我们称为迭代(Iteration)。在Python中,迭代是通过for ... in
来完成的。 - Python的
for
循环抽象程度要高于C的for
循环,因为Python的for
循环不仅可以用在list或tuple上,还可以作用在其他可迭代对象上。 - dict的迭代,参见如下代码:
>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> for key in d:
... print(key)
...
a
c
b
因为dict的存储不是按照list的方式顺序排列,所以,迭代出的结果顺序很可能不一样。默认情况下,dict迭代的是key。如果要迭代value,可以用for value in d.values()
,如果要同时迭代key和value,可以用for k, v in d.items()
。
- 通过isinstance()方法和collections模块的Iterable类型可以判断一个对象是否可以迭代
>>> from collections import Iterable
>>> isinstance('abc', Iterable) # str是否可迭代
True
>>> isinstance([1,2,3], Iterable) # list是否可迭代
True
>>> isinstance(123, Iterable) # 整数是否可迭代
False
- Python内置的
enumerate
函数可以把一个list变成索引-元素对,这样就可以在for
循环中同时迭代索引和元素本身。
>>> for i, value in enumerate(['A', 'B', 'C']):
... print(i, value)
...
0 A
1 B
2 C
3. 列表生成式(list comprehensions)
- 运用列表生成式,可以快速生成list,可以通过一个list推导出另一个list,而代码却十分简洁。
>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
- for循环后面还可以加上if判断
>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]
- 还可以使用两层循环,可以生成全排列
>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
- 列出当前目录下(.)的所有文件和目录名
>>> import os # 导入os模块,模块的概念后面讲到
>>> [d for d in os.listdir('.')] # os.listdir可以列出文件和目录
['.emacs.d', '.ssh', '.Trash', 'Adlm', 'Applications', 'Desktop', 'Documents', 'Downloads', 'Library', 'Movies', 'Music', 'Pictures', 'Public', 'VirtualBox VMs', 'Workspace', 'XCode']
for
循环其实可以同时使用两个甚至多个变量,比如dict
的items()
可以同时迭代key和value,因此,列表生成式也可以使用两个变量来生成list。
>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
>>> [k + '=' + v for k, v in d.items()]
['y=B', 'x=A', 'z=C']
- 把一个list中所有的字符串变成小写
>>> L = ['Hello', 'World', 'IBM', 'Apple']
>>> [s.lower() for s in L]
['hello', 'world', 'ibm', 'apple']
如果list中既包含字符串,又包含整数,由于非字符串类型没有lower()
方法,所以会报错。对此我们可以使用内建的isinstance
函数判断一个变量是不是字符串
>>> x = 'abc'
>>> y = 123
>>> isinstance(x, str)
True
>>> isinstance(y, str)
False