0%

Python 基础语法

关于 Python 系列其他文章的传送门

  1. 《 Python 基础语法 》      您当前所在位置

  2. 《 Python 进阶:网络、多线程、异步、数据库、WEB 》

  3. 《 Python 和 RabbitMQ 的那点事 》

  4. 《 Python 和 Redis 的那点事 》

  5. 《 Python 爬虫基础和包 XPath、BeautifulSoup4、PhantomJS、Selenium 》

  6. 《 边爬豆瓣,边学习 Python 爬虫框架 Scrapy 》

  7. 《 用了 Scrapy-Redis 分布式爬虫后,一个能打的都没有! 》

  8. 《 全栈开发入门 JavaScript、React 和 Django 》

  9. 《 Django REST framework 的那点事 》



logo

一、初识 Python

python 程序和 java 一样,编译后的二进制程序是运行在虚拟机中 pvm,而虚拟机的是用 C 开发的。

CPython: 原始、标准的实现方式;
JYthon: 用于与 Java 语言集成的实现;
IronPython: 用于与 .NET 集成实现。

Python 程序的执行:
interpreter

python34-devel: 编译时用到的文件;
python34-lib: python3 的标准库;
python34-tools: 工具。

1
yum install python34 python34-devel python34-libs python34-tools
1
2
3
4
5
~]# python3.4
>>> print("hello word")
hello word

>>> exit()

Python 的特点

Python 是动态类型的编程语言,一切皆对象。变量名是在 Python 的内存中单独存放的,而变量值也存在内存中。变量名会通过指针指向变量值,变量值可以被赋值用来保存数据。动态类型指的就是在运行的过程中,可以按需修改变量名对变量值的引用。

所以 ab 如果值都为 15,那么可能这两个变量名引用的是同一个变量值,如果此时通过 a 修改为 30,那么 b 看到的也是 30

变量名没有类型,可以随时指向不同的数据对象。只有变量值本身才有类型。

程序都是指令+数据组成:

  • 过程式编程:以指令为中心,数据服务于指令需要;
    • 先选择路线,再填充需要交通工具;
  • 对象式编程:以数据(对象)为中心,指令服务于数据。先定义跟多类,然后通过对类中的属性赋值并实例化出来,而把类实例化的过程被称之为创建对象的过程;更适合编写大段代码;
    • 先选择好要使用的交通工具,再决定路线;

Python 能够完整支持上面的两种思路,并可以将两者结合起来。

1
2
3
4
5
6
7
8
9
10
11
12
>>> a=123
>>> b=456
>>> a+b
579 # 结果如果没有保存到变量中,那么就仅出现这一次,就没了

>>> c=a+b # 这样保存为变量后,会将结果记录下来 # 等同于 c=a.__add__(b)
>>> print(c)
579

>>> d=a.__add__(b)
>>> print(d)
579

核心数据类型,支持:

  • 数值:
  • 字符串:
  • 列表:
  • 字典:
  • 元组:
  • 文件:
  • 其他类型:集合、类类型、Node、布尔型

数值类型:

  • 整数
  • 浮点数
  • 复数

字符类型:

  • 字符串:由特定顺序的字符所组成的字符序列:
    • 和数值一样属于不可变对象;
    • 支持使用 ‘’“” 赋值,并且支持使用 ‘’‘“”“ 来赋值一段数据,也就是数据可以换行;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 字符串可以做乘法
>>> a='hello'
>>> a * 5
'hellohellohellohellohello'

# 字符串可以做加法
>>> w='world'
>>> a+w
'helloworld'

# 获取字符串长度
>>> len(w)
5

# 成员关系判断,判断变量 a 中是否有 ”he” 这两个字符
>>> 'he' in a
True

# 所以字符换成大写或小写
>>> a.lower()
'hello'
>>> a.upper()
'HELLO'

Python 程序文件

将编写的程序保存至以 .py 结尾的文件中,方便多次运行。

  • 在 Python 中,这类包含了一系列预编写好的语句的程序文件称为模块;
  • 能够直接运行的模块文件通常被称作脚本(即程序的顶层文件)

示例:

1
2
3
#!/usr/bin/python3.4
import platform # 加载模块
print(platform.platform()) # 打印 platform 模块中的 platform

运行时会创建一个编译文件 .pyc,为了加快速度,被调用模块的编译文件不会被删除,而顶层文件的编译文件在执行完后会被删除。

Python 程序可以分解成模块、语句、表达式和对象:

  • 程序有模块构成;
  • 模块包含语句;
  • 语句包含表达式;
  • 表达式建立并处理对象;
    • 表达式是“某事”,而语句是“做某事(即指令)”
      • 例如:3+4 是某事,而 print 3+4 是做某事;
    • 语句的特性:它们改变了事物,例如复制语句改变了变量,print 语句改变了屏幕输出等。

二、过程式编程

数据结构

Python 中最基本的数据结构就是序列。

序列中的每个元素被分配一个序号,即元素的位置,也称为索引:

  • 从 0 开始编号,从左至右;
  • 从 -1 开始编号,从右至左。

Python 中包含 6 种内键的数据序列:列表、元祖、字符串、Unicode 字符串,buffe 对象和 xrange 对象。

数据类型

基本数据类型:

  • 整数;
  • 浮点;
  • 字符串:必须使用引号。

组合数据类型;

  • 序列:
    • 列表:[]
    • 元组:()
    • 字符串:"
  • 整合;
  • 映射。

使用序号引用:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> l1 = ["mon","tue","wed"]
>>> l1[0]
'mon'
>>> l1[1]
'tue'
>>> l1[2]
'wed'

>>> s = "hello"
>>> s[1]
'e'
>>> s[2]
'l'

多维引用:

1
2
3
4
5
6
>>> l2 = [["red","yello","balck"],["mon","tue","wed"]]
>>> l2[0]
['red', 'yello', 'balck']
>>> l2[0][1]
'yello'
>>>

列表中可以有不同类型的元素:

1
2
3
4
5
>>> l3 = [123,"abc"]
>>> type(l3[0])
<class 'int'>
>>> type(l3[1])
<class 'str'>

逻辑操作符

Python 提供了4组逻辑运算:

  • 身份操作符:
    • is:判定左端对象引用是否相同于右端对象引用;
  • 比较操作符:
    • <,>,<=,>=,!=,==
  • 成员操作符:
    • innot in:测试成员关系;
  • 逻辑运算符:
    • and,or,not

控制流语句

if
while
for…in
try

算数操作符

除了常见的算数操作集,很多 Python 数据类型也可以使用增强的赋值操作符,如 +=,-= 等。

通用的功能,使用增强型赋值操作符的性能较好。

int 类型是不可变的,所以增强型赋值实际的过程是创建了一个新的对象来存储结果后,将变量名执行了重新绑定。

输入输出

输出:print()
输入:input()

sys 模块中提供了 3 中标准文件对象:

  • 标准输入:sys.stdin
  • 标准输出:sys.stdout
  • 标准错误:sys.stderr

三、函数的创建与调用

使用 def 语句定义函数;

1
2
def functionName(arg):
suite

如果函数没被调用则不会运行函数的主体语句,但是会运行定义函数的 def 语句。

检测函数是否可被调用:

1
callcable(a)

Python 内置函数

Python 的标准库有众多的内置模块,这些模块拥有大量的函数。

  • Python 模块实际上就是包含 Python 代码的 .py 文件,其拥有自定义的函数与类,以及变量;
  • 导入模块使用 import 语句进行,后面跟模块名称;
  • 导入一个模块后,可以访问其内部包含的任意函数、类、变量等。

导入一个随机数模块的示例:

1
2
3
4
5
6
7
8
9
>>> import random

>>> x=random.choice(['a','b','c','d','e'])
>>> print(x)
b

>>> x=random.choice(['a','b','c','d','e'])
>>> print(x)
c

语句和语法

  • 注释:#
  • 续行:\
  • 闭合操作符,单一语句跨行:'''
  • 代码组:
    • 缩进相同的一组语句构成一个代码块;
    • 首行以关键字开始,如 ifwhile 等,以冒号结束;
    • Python 使用缩进来分割代码组,同一代码组的代码行必须严格左对齐,否则会造成语法错误。
  • 同一行可以放置多个语句,使用 ; 分隔;

主程序

无论当前模块是被别的模块导入还是作为脚本直接执行,都会执行这部分代码。

  • 最高级别的语句(没有缩进的),在模块被导入时就会执行,无论是否真的需要执行。因此:
    • 仅在主程序模块中编写大量的顶级可执行代码;
    • 用于被导入的模块只应该存在较少的顶级执行代码。

序列切片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
>>> print(l1)
['mon', 'tue', 'wed']

>>> print(l1[0:2]) # 显示从第 0 到第 1 个元素,因为是半开半闭区间,左闭右开,所以要写到 2
['mon', 'tue']

>>> print(l1[1:]) # 显示从 1 之后的所有元素
['tue', 'wed']
>>>

>>> print(l1[:2]) # 显示第 2 个之前的元素,不包含第 2 个
['mon', 'tue']

>>> print(len(l1)) # 显示列表中的元素个数
3

# 多个列表之间相加
>>> print(l1)
['mon', 'tue', 'wed']
>>> print(l2)
[['red', 'yello', 'balck'], ['mon', 'tue', 'wed']]
>>> print(l1+l2)
['mon', 'tue', 'wed', ['red', 'yello', 'balck'], ['mon', 'tue', 'wed']]

# 列表乘法
>>> print(l1 * 3)
['mon', 'tue', 'wed', 'mon', 'tue', 'wed', 'mon', 'tue', 'wed']

# 成员关系判断
>>> x = "mon"
>>> x in l1 # 判断 x 是否在 l1 序列中
True

# 列表支持原处修改
>>> l1.pop() # 弹出第一个元素(右)
'wed'
>>> print(l1)
['mon', 'tue']

>>> l1.append('Thu') # 追加一个元素(右)
>>> print(l1)
['mon', 'tue', 'Thu']

# 把其他列表合并进来
>>> l1.extend(l2) # 把 l2 列表中的元素合并进来,并且 l2 还在
>>> print(l1)
['mon', 'tue', 'Thu', ['red', 'yello', 'balck'], ['mon', 'tue', 'wed']]

# 逆序排列元素
>>> print(l1)
[['mon', 'tue', 'wed'], ['red', 'yello', 'balck'], 'Thu', 'tue', 'mon']

# 删除指定的元素
>>> del l1[0:1] # 从 0 到 1
>>> print(l1)
[['red', 'yello', 'balck'], 'Thu', 'tue', 'mon']

字典

由多个键值对组成的集合就叫做字典。

在引用已有序列时是使用索引,而引用已有字典是使用键。

1
2
3
4
5
>>> d1 = {'name': 'Jerry', 'age': 18, 'title': 'UFO'}
>>> d1['name']
'Jerry'
>>> d1['age']
18

字典也是可变对象,可以被重新赋值,内部是新建了一个值并重新引用:

1
2
3
4
5
>>> d1['age']
18
>>> d1['age'] = 21
>>> d1['age']
21

其他操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 列出所有键
>>> d1.keys()
dict_keys(['name', 'age', 'title'])

# 列出所有值
>>> d1.values()
dict_values(['Jerry', 21, 'UFO'])

# 列出所有键值
>>> d1.items()
dict_items([('name', 'Jerry'), ('age', 21), ('title', 'UFO')])

# 弹出指定的键值
>>> d1.pop('title')
'UFO'
>>> d1
{'name': 'Jerry', 'age': 21}

# 随机弹出键值
>>> d1.popitem()
('name', 'Jerry')
>>> d1
{'age': 21}

集合

通常被拿来做交叉并补运算。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> s1 = set('hello')
>>> s2 = set('world')
>>> print(s1)
{'o', 'e', 'h', 'l'}
>>> print(s2)
{'o', 'r', 'l', 'd', 'w'}

# 并集
>>> s1.union(s2)
{'o', 'd', 'h', 'l', 'r', 'w', 'e'}

# 交集
>>> s1.intersection(s2)
{'o', 'l'}

深复制、浅复制

列表、字典、元组可以在不使用续行符的情况下位于多行上,因为他们有开闭两个符号。

每一个对象都有引用计数器,每次新做一次赋值时,引用的次数都会增加。如果引用的次数为 0 就会被垃圾收集器自动回收。

因为列表和字典是可变的,所以有两种复制方式,浅复制和深复制:

  • 浅复制:两个元素引用同一个对象,当对象改变时两个元素都受影响;l1 = a,l2 = l1
  • 深复制:两个元素引用不同的对象;深复制需要使用 copy.deepcopy() 模块;

每个对象都有一个唯一的身份标识,也就是内存地址,查看方式:

1
2
>>> id(l1)
139748843550280

浅复制的内存地址是一样的。

is 比较

  • l1 == l2:对象值的比较;
  • l1 is l2:是否为同一对象;
  • type(l1) is type(l2):两个对象的类型是否相同。

四、Python 中的比较操作

Python 中不同类型的比较方法

  • 数字:通过相对大小进行比较;
  • 字符串:按照字典次序逐字符进行比较;
  • 列表和元组:自左至右比较各部分内容;
  • 字典:对排序之后的(键、值)列表进行比较;

Python 中真和假的含义

  • 非零数字为真,否则为假;
  • 非空对象为真,否则为假;
  • None 则始终为假。

if 语法示例

基本:

1
2
3
4
5
6
7
8
9
10
11
12
>>> if l1 is l3:
... print('same')
... else:
... print('diff')
...
diff


>>> if l1 is not l3:
... print('diff')
...
diff

三元语法表达式:

1
2
3
4
5
>>> a=12
>>> b=17
>>> max = a if a>b else b
>>> print(max)
17

while 语法示例

在 Python 中的 while 可选择使用 else 分支,只要循环条件为假时并且循环是正常终止的,就会执行 else:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> sum = 0
>>> i = 0
>>> while i <= 100:
... sum += i
... i += 1
... else:
... print(sum)
...
5050

>>> i = 1000
>>> while i <= 100:
... print('ok')
... else:
... print('finished')
...
finished
  • break:跳出所处的最近层循环;
  • continue:跳到所处的最近层循环的开始处;
  • pass:占位语句,当语法需要语句但还没有任何实用语句可写时使用。

for 循环语法格式

1
2
3
4
for EXPRESSION1 in ITERABLE:
for_suite
else:
else_suite

通常 EXPRESSION 是一个单独的变量,或是一个变量序列,一般以元组的形式给出。

如果以元组或列表用于 EXPRESSION,则其中的每个数据项都会拆分到表达式的项,例如:

1
2
3
4
5
6
7
8
>>> t=[(1,2),(3,4),(5,6),(7,8)]
>>> for (a,b) in t:
... print (a,b)
...
1 2
3 4
5 6
7 8

注意:for 循环比 while 循环执行速度更快。

range

语法:

1
2
range(stop)
range(start, stop[, step])

Python 3 提供一个内置函数(Python 2 是两个),用于在 for 循环中定制特殊的循环:
range:一次性地返回连续的整数列表;

1
2
3
4
5
6
7
8
>>> for i in range(4):
... print(i)
...
0
1
2
3
4

zip

取得一个或多个序列为参数,将给定序列中的并排元素配成元组,返回这些元组的列表:

1
2
3
L1 = [1,2,3,4,5,6,7]
L2 = ['a','b','c','d','e','f','g']
zip(L1,L2)

五、Python 迭代器和列表解析

迭代

迭代:支持每次返回自己所包含的一个成员对象。

可迭代对象:

  • 序列:list、srt、tuple;
  • 非序列:dict、file

迭代器

迭代器(iterator)又称游标(cursor),她是可以在一个容器物件(如列表)上实现元素遍历的借口。

使用 iter() 命令可从任何序列对象中得到迭代器:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> l1 = ['h','e','l','l','o']    # 创建一个列表
>>> a = iter(l1) # 从 l1 中的得到迭代器,并赋值给变量 a
>>> a.__next__() # 使用 __next__ 命令,每次通过迭代器读取一个元素
'h'
>>> a.__next__()
'e'
>>> a.__next__()
'l'
>>> a.__next__()
'l'
>>> a.__next__()
'o'
>>>

列表解析

列表解析就是根据一个表达式生成一个列表。

表达式的示例,含义:给 l5 映射一个从 0 到 50 的所有偶数列表:

1
2
3
4
>>> l5
[0, 5, 10, 15, 20, 25, 30, 35, 40, 45]>>> l5 = [ x for x in range(0,50,2)]
>>> l5
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48]

表达式含义:从 0 到 10 所有数的平方为列表,映射给 l6:

1
2
3
>>> l6 = [ x**2 for x in range(10)]
>>> l6
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

表达式含义:把从 0 到 10 的所有数对 2 取模,结果为 1 的数为列表,映射给 l7:

1
2
3
>>> l7 = [ x for x in range(10) if x%2 == 1 ]
>>> l7
[1, 3, 5, 7, 9]

生成器

生成器表达式并不真正创建数字列表,而是返回一个生成器对象,此对象在每次计算出一个条目后,把这个条目“产生”出来。

  • 生成器表达式使用了“惰性计算”或称作“延迟求值”的机制。

当序列过长,并且每只只需要获取一个元素时,应当考虑使用生成器表达式而不是列表解析,以节省内存。

  • 生成器表达式于 Python 2.4 引入;

语法:

1
2
(expr for iter_var in iterable)
(expr for iter_var in iterable if cond_expr)

示例,一次只返回一个数,不像列表解析那样一次性全部显示:

1
2
3
4
5
6
7
8
9
10
11
>>> l8 = ( x for x in range(10) if x%2 == 1 )
>>> l8.__next__()
1
>>> l8.__next__()
3
>>> l8.__next__()
5
>>> l8.__next__()
7
>>> l8.__next__()
9
  • 列表解析使用:[ ]
  • 生成器使用:( )

六、文件

使用 open() 可以创建一个文件对象。访问模式:

  • r:只读;
  • w:覆盖写;
  • a:追加;
  • +:可读可写;
  • b:二进制格式;
  • t:文本格式,默认。

缓冲:

  • 0:禁用缓冲;
  • 1:只缓冲一行;
  • 2:数字是几则为缓冲几行。

文件方法:

  • f1.close():关闭文件;
  • f1.fileno():返回文件的描述符;
  • f1.flush():把缓冲的数据刷写到磁盘中;
  • f1.isatty():判断 f1 是否为 tty 设备;
  • f1.next():一次返回一行;
  • f1.seek(offset,whence=0):在文件中移动文件指针。含义:从 whence 指定的位置开始,偏移 offset 个字节;
    • whence 的取值:
      • 0:文件头;
        • 移动到文件头部:f1.seek(0,0)
      • 1:当前位置;
      • 2:文件尾;
  • f1.tell():显示当前指针在第几个字节;
  • f1.write():向文件写入数据;
  • f1.writelines():向文件写入字符串序列。
  • f1.read():读一个文件的指定字节数;

读取一个文件的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
~]# cp /etc/fstab /tmp

>>> f1 = open('/tmp/fstab','r')

# 一次只读文件的一行
>>> f1.readline()
'\n'
>>> f1.readline()
'#\n'
>>> f1.readline()
'# /etc/fstab\n'
>>> f1.readline()
'# Created by anaconda on Thu May 30 05:21:36 2019\n'

# 一次显示所有的行。结果是以列表的形式返回,每个元素是文件中的一行
>>> f1.readlines()
['#\n', "# Accessible filesystems, by reference, are maintained under '/dev/disk'\n", '# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info\n', '#\n', '/dev/mapper/centos-root / xfs defaults 0 0\n', 'UUID=780667a3-0b8c-4c2c-94d2-77bad2e05c76 /boot xfs defaults 0 0\n', '/dev/mapper/centos-swap swap swap defaults 0 0\n']

使用覆盖写的方式创建一个文件的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> srt1 = 'hellp fatab'

# 以覆盖写方式打开文件
>>> f1 = open('/tmp/fstab','w')

# 写入数据
>>> f1.write(srt1)
11

# 现在虽然写入数据了,但是还在缓冲区内,所以要刷写到磁盘
>>> f1.flush()
>>> f1.close()

# 去看文件中已经有内容了,因为使用了覆盖写的方式,所以文件之前的内容都被清空了
~]# cat /tmp/fstab
hellp fatab#

os 模块

Python 对 IO 的访问主要通过 os 这个模块:

1
2
3
4
import os

# 显示帮助信息
print(os.__doc__)

os 中有很多子模块,比如 os.path,主要负责对路径操作。

os 模块的文件处理:

  • mkfifo()/mknod():创建命名管道文件/创建设备文件;
  • remove():删除文件;
  • rename():重命名;
  • unlink():删除链接文件;
  • stat():获取文件状态信息;
  • symline():创建符号链接;
  • utime:更新文件时间戳;
  • tmpfile():创建并打开一个临时文件;
  • walk():类似 tree 命令,生成一个目录下的所有文件名和目录名;
  • chdir:同 cd 命令;
  • chroot():同 chroot 命令;
  • listdir():同 ls 命令;
  • getcwd():同 pwd 命令;
  • mkdir/makedirs:同 mkdir 命令;
  • rmdir/removedirs():同 rmdir 命令:
  • access():判断访问权限;
  • chmod():同 chmod 命令;
  • chown():同 chown 命令;
  • umask():设置文件的 mask
  • open():同 rmdir 命令;
  • read()/write():同 rmdir 命令;

os.path 模块的路径处理:

  • basename():级名;
  • dirname():路径名:
  • getatime():获取文件的访问时间;
  • getmtime():获取文件的修改时间;
  • getctime():获取文件的创建时间;
  • exists():判断文件是否存在;
  • isabs():判断是否为绝对路径;
  • isdir():判断是否为目录;
  • isfile():判断是否为文件;
  • islink():判断是否为链接;
  • ismount():判断是否为挂载点;
  • samefile():判断两个文件是否指向同一个文件;

持久存储

数据如果想要持久存储,需要存在磁盘中。而内存中的数据需要经过数据序列化后保存到磁盘上,以后才能正确的把数据原样的组合起来,也称之为数据扁平化。

七、Python 函数

在 Python 中有 4 中函数:

  • 全局函数:定义在模块中;
  • 局部函数:嵌套于其他函数中;
  • lambda 函数:表达式;
  • 方法:与特定数据类型关联的函数,并且只能与数据类型关联一起使用。

定义函数语法:

1
2
def functionName(parameters):
suite

Python 脚本在运行到函数代码时,只会运行 def 这一行语句把函数对象创建出来,而不会真的执行函数体;

def 创建了一个对象并将其赋值给一个变量名(即函数名);

return 用于返回结果对象,其为可选;当无 return 语句的函数会自动返回 None 对象;

def 语句在运行之后,可以在程序中通过函数后附加括号进行调用,并可以在括号内传递参数。

名称空间

Python 中有名称空间的概念,创建、改变或查找变量名都是在名称空间中进行的。

在代码中变量名被赋值的位置决定了其全局的作用域。

函数定义了本地作用域,模块定义了全局作用域。

每一个模块是一个文件,是一个全局作用域,全局作用域的范围仅限于单个程序文件,在模块内可以直接用变量名来引用。但当模块被引用时,则要使用模块名+变量名才能使用。比如在 copy 模块中有一个变量 a,当在本模块中可以直接通过 a 来引用,但是在其他地方使用这个模块时,则要使用 copy.a 来引用变量。

每次对函数的调用都对创建一个新的本地作用域,赋值的变量除非声明为全局变量,否则均为本地变量。

所有的变量名都可以归纳为本地、全局或内置。

变量名引用在三个作用域中按序查找:本地、函数内、全局、内置。即范围约小,优先级越高。

全局变量

全局变量是模块文件内部的顶层的变量名;

如果要在函数内部对全局变量进行复制的话,必须事先使用 global 进行声明;

  • global 关键字后跟一个或多个由逗号隔开的变量名;

全局变量名在函数内部不经声明也可以被引用;

本地变量在函数返回时会消失,而全局变量不会,因此,使用全局变量是保存函数状态信息的最直接办法;

应尽力避免使用全局变量。

参数传递

当变量的类型是可变时,用两种方法可以避免可变参数被函数修改:

  • 直接传递可变对象的副本:testFunc(A,B[:])
  • 在函数内部创建可变参数的副本:B=B[:]

参数匹配模型

默认情况下,参数通过其位置进行传递,从左至右。所以必须精确地传递和函数参数一样多的函数;

也可以通过关键字参数、默认参数、参数容器等改变这种机制。

关键字参数

使用 name=value 的语法通过参数名进行匹配:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> def f1(a,b,c):
... print(a)
... print(b)
... print(c)
...
>>> f1(1,2,3)
1
2
3
>>> f1(b=1,a=2,c=3)
2
1
3

默认参数

定义函数是指定默认值,那么引用时可不传递相同数量的参数,会直接使用默认值。

注意:带默认值的参数必须处于最右侧。

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> def f1(a,b=32,c=11):
... print(a)
... print(b)
... print(c)
...
>>> f1(a=23)
23
32
11
>>> f1(a=23,b=8787)
23
8787
11

可变参数

  • 定义函数时使用 * 开头的参数,在使用函数时可传递任意数量的参数,会自动识别为元组。
1
2
3
4
5
6
7
8
9
>>> def f1(*args):
... print(args)
...
>>> f1(1)
(1,)
>>> f1(1,2)
(1, 2)
>>> f1(1,2,3,4,5)
(1, 2, 3, 4, 5)
  • 而使用 ** 的会被识别为字典。
1
2
3
4
5
>>> def f1(**args):
... print(args)
...
>>> f1(a=1,b=2,c=3)
{'b': 2, 'c': 3, 'a': 1}

上面是在定义函数是使用 *,其实也可在传递参数时使用 *

定义一个函数,以此为基础:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> def f1(a,b,c):
... print(a,b,c)
...
>>> a1=(1,2,3)

1.把变量的值展开为元素,并对位传递进去:
>>> f1(a1) # 如果直接传递参数 a1,会提示参数数量不足
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f1() missing 2 required positional arguments: 'b' and 'c'

>>> f1(*a1) # 传递是加 *,可对位展开
1 2 3
>>>

2.把字典作为参数传递:

1
2
3
4
>>> d1 = {'a':123,'b':456,'c':789}

>>> f1(**d1) # 传递时使用 **,可将字典也对位传递
123 456 789

3.还可以混合使用:

1
2
3
4
>>> d1 = {'b':111,'c':222}

>>> f1(000,**d1)
0 111 222

装饰器

是一个函数,用途是包装另一个函数,以实现修改或增强被包装对象的功能

@装饰器名称,必须出现在函数的定义之前。

八、模块

模块也是一个对象

模块也是一个容器,容器内可以保存很多对象,对象可以通过 import 加载。

1
2
import copy
copy

模块自己定义了一个容器,容器内有许多属性,属性就是在模块内定义的全局变量或其他的名称,全局变量可能是整型、浮点等,只要是顶格定义的都可以理解为全局对象。

获取当前所有可用的模块列表:

1
2
3
4
5
6
7
help("modules")

# 以 copy 模块为例
copy.__dict__ # 查看与模块相关的所有字典

copy.__doc__ # 查看模块的文档
copy.__file__ # 查看模块文件路径

Python 的程序架构:由一个顶层文件,以及一个或多个模块文件组成

导入模块:

  • import:导入指定的模块整体;并会引入新的名称空间,即模块名;
1
2
3
4
5
6
7
import copy
copy.deepcopy()

# 支持同时导入多个模块
import mode1,mode2,mode3...
# 支持在导入时为模块创建别名
import mod1 as mod1_alias
  • from-import:导入指定的模块的部分属性;把模块的属性直接引入到当前名称空间;
1
2
from copy import deepcopy
deepcopy()

模块搜索路径:

  • 程序的主目录:
  • PYTHONPATH 变量中定义的各目录;
  • 标准链接库的目录:
  • 任何以 .pth 结尾的文本文件中定义的路径;

结合起来在 sys.path 模块里所包含的路径:

1
2
3
>>> import sys
>>> print(sys.path)
['', '/usr/lib64/python34.zip', '/usr/lib64/python3.4', '/usr/lib64/python3.4/plat-linux', '/usr/lib64/python3.4/lib-dynload', '/usr/lib64/python3.4/site-packages', '/usr/lib/python3.4/site-packages']

Python 包

将一组模块归并到一个目录,此目录即为包,必须放置在模块的搜索路径下;

1
import dir1.dir2.mod1

九、面向对象编程(OOP)

类就是用户自定义的一种可以保存特定数据格式的类型,对这个类型中的属性赋值,能实例化出一个具体的对象。而这个对象能支持什么样的操作,是用户自己设计的。

类之间的关系:

  • 依赖 uses-a:一个类的方法操纵另一个类的对象;
  • 聚合 has-a:类 A 的对象包含类 B 的对象;
  • 继承 is-a:描述特殊与一般关系。

创建类(class)

为了区分函数和类,一般情况下类的首字母要大写,而函数不需要:

1
2
3
class ClassName(bases):  # 括号内指定基类,没有可不指定
'class documentation string' # 类的说明文档
class_sulte

示例:

1
2
3
4
5
6
7
8
9
# 创建类
>>> class FirstClass():
... 'my first class'
... pass
...

# 调用创建的类,并查看类文档
>>> FirstClass.__doc__
'my first class'

类的主体可以包含:声明语句、类成员定义:

  • 任何赋值语句都会创建类属性;
  • 如果不存在继承关系,那么基类可以为空;
  • 类本身是可执行代码,直到运行后类对象才会存在;
  • 实例化的方法:instance_name = ClassName()
    • 每个实例化出来的对象,都会继承类的属性,并获得自己的名称空间;

在 Python 中,class 语句类型 def,是可执行代码:

1
2
3
4
5
6
7
8
9
              # ↓ 类名
>>> class FirstClass:
... spam = 30 # 类数据属性
... def display(self): # 类方法,属于可调用的属性
... print(self.spam)
...
>>> x = FirstClass() # 创建类的实例
>>> x.display() # 方法调用
30

通过实例调用的方法,相当于调用所属类的方法来处理当前实例:

  • 类似 instance.method(args) 会被自动转换为 `class.method(instance,args);
  • 所以上面例子中的 x.display 会被自动转换为 FirstClass.display(x),即调用类的方法来处理实例 x
  • 因此,类中的每个方法必须具有 self 参数,为了隐藏当前实例;

统计 Nginx 的客户端数量

使用字典,实现统计客户端数量的功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 初始化字典
ips = {}

# 把日志都读取出来文件,保存在 logs 这个列表中,日志量大时慎用
logs = open('/var/log/nginx/access.log','r').readlines()

for line in logs:
ip = line.split(" ")[0] # 可以使用 srt.split 方法对字符串指定以”空格“做切分,切分后会生成新的列表,列表中的每段数据为一个元素,取第 0 号元素
if 7 <= len(ip) >= 15: # 判断元素的字节数在 7 到 15 之间
# 字典是可变对象,可以通过以下方式对字典中的元素重新赋值。
# 以 IP 地址为键名,出现的次数为值,每获取一次后加 1。实现了在 ips 这个字典中,如果以 IP 为名的键出现过,就把键对应的值 +1,如果没出现过,就使用 0 作为值后再加 1,实现计数
# 对字典来说有个方法叫 get:D.get(k[,d]),含义是在 D 这个字典中如果已经有一个健名为 k,则取 D[k] 这个键本来的值,否则就取值为 D[d]。
ips[ip] = ips.get(ip,0) + 1
print(ips) # 打印结果