Python 笔记(一)

2019-01-29  本文已影响59人  Python高效编程

Written by Python高效编程

应用

流程:写程序/编译/测试/重编译 过程漫长

shell 适合移动、修改文件,但不适合用户界面或者游戏

c/c++/Java 耗时过长,即使是初稿

Python 使用起来更简单,跨平台,帮助你更快速地完成工作。

Python 虽然简单易用,但也是通用编程语言。相较于其他脚本语言,Python 提供了多得多的适合于大型项目的结构与支持。作为高级语言,Python 相较于 C 语言提供更多的错误检查,并且有高度封装的数据类型,比如灵活的矩阵和字典。Python 相较于 Awk 甚至是 Perl,事何更加复杂的领域,尽管许多方面使用起来差不多方便。

Python 允许你把程序分割成可以重复使用的模块,你可以将模块用在其他 Python 项目中。你还可以使用 Python 内置的标准模块,或者作为开始学习 Python 的范例。一些模块提供:文件读取写入,系统调用,sockets编程,甚至是用户界面接口工具 TK。

Python 是解释型语言,编译和linking是不必要的,在程序开发的时候,可以节省可观的时间。你可以通过交互式终端执行 Python 程序,因此很容易就可以实验语言的特性,写一些一次性的程序,或者在自下而上开发时测试函数。同时,Python 是容易使用的桌面计算器。

可扩展性强

非正式介绍

字符串

P y t h o n
0 1 2 3 4 5 6
-6 -5 -4 -3 -2 -1

列表

更加复杂的任务

# 斐波那契数列
a, b = 0,1
while b < 10:
    print(b)
    a, b = b, a + b

if 语句

for 语句

words = ['cat', 'window', 'defenestrate']
for w in words[:]:  
    if len(w) > 6: 
        words.insert(0, w)

range() 函数

它是一个当你迭代它时,返回迭代序列连续元素的对象。但是它并不生成列表,因此节省了空间。这样的对象是可迭代的,这种结构的特点是:你可以连续从中获取元素,直到没有为止。for 语句是迭代器,函数list()也是迭代器,它可以从可迭代对象中创建列表

break continue else

for n in range(2, 10):
    for x in range(2, n):
        if n % x == 0:
            print(n, 'equals', x, '*', n//x)
            break
    else:
           # loop fell through without finding a factor
        print(n, 'is a prime number')

pass

定义函数

def fib(n):
    a, b = 0, 1
    while a < n:
        print(a, end=' ')
        a, b = b, a+b
    print()

更准确地说,在函数中分配的变量,储存在局部符号表格中(locak symbol table)

变量引用查找局部变量符号表格,然后是封闭函数的局部符号表格,接着是全局符号表格,最后是内置函数名。因此,全局变量不能直接在函数中直接赋值给全局变量(除非使用 global 语句),尽管我们可以引用全局变量。

函数的实参,在被调用函数的局部符号表格中。因此函数传递参数的引用,而不是参数的值。数值不会被改变(常量),列表会被改变。

当函数调用另一个函数,这次调用会创建新的局部符号表格。

函数定义在当前符号表格中引进了新的函数名。函数名的值的类型被解释器当成用户定义函数。这个值可以分配其他名字,我们可以通过其他名字调用这个函数,这就是重命名机制的原理:

fib
f = fib
f(100)

函数没有 return 语句也会返回值,return None,None 被解释器抑制了。如果它是唯一写入的值,对于交互模式解释器,使用print

更多关于定义函数的知识

默认参数值

i = 5

def f(arg=i):
    print(arg)

i = 6
f()
# 打印 5

警告:

初始值只被计算一次。这使得当初始值是可变对象(列表,字典,或者大多数类),会有一些不同。举个例子,下面的函数会累计传递给序列的参数。

def f(a, L=[]):
    L.append(a)
    return L

print(f(1))
print(f(2))
print(f(3))
输出:
[1]
[1, 2]
[1, 2, 3]

def f(a, L=None):
    if L is None:
        L = []
    L.append(a)
    return L

关键字(Keyword Arguments)

通过使用关键字参数(kwarg=value),我们可以调用函数。

*arguments **keywords

def f(*age,**name):
    print(age)
    print(name)
f(1,4,'sd',key = 'sd',time = '12')

任意参数列表

def concat(*args, sep="/"):
    print(sep.join(args))
    
concat("earth", "mars", "venus", sep=".")
concat("earth", "mars", "venus")

解包函数参数列表

相反的情况,就是参数已经在列表或者元组中了,但是函数调用需要分离的位置参数,这是我们就要将列表或者元组解包。举个例子,内置函数 range 需要 start 和 stop 参数。如果这些参数不能被分别得到,我们就要用到 * 操作符进行解包来获取列表或者元组中的参数。相似的,我们可以使用 **dict 来获取字典中元素。

a = {'name':'xkk', 'age':18, 'hobby': 'read'}
f(**a)

lambda 表达式

小型匿名(anonymous)函数,可以使用关键字 lambda。这个函数返回两个关键字的和:lambda a, b: a+b。lambda 函数可以在需要函数对象的地方使用。在语法上,它被限制为单个表达式。从语义上讲,它们正常定义函数的语法糖(syntactic sugar),类似于内置函数定义,lambda 函数可以引用包含范围内的变量。

>>> pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
>>> pairs.sort(key=lambda pair: pair[1])
>>> pairs
[(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]

文档说明

下面是一些关于说明文档内容与格式的惯例。

第一行因该是对于对象目标简短而准确的介绍。简单地说,它不应该明确说明对象的名字和类型,因为这些可以通过其他方式介绍,除非对象名碰巧是描述函数操作的动词。这一行应该以大写字母开头,以句号结束。(end with a period)

如果说明文档有多行,那么第二行应该是空白行,视觉上分割总结与其余部分。接下来的内容,应该是一段或者更多段落,描述调用惯例和副作用(side effects)。

Python 解释器并不会从多行字符文字中去除缩进。所以如果需要的话,处理文本的工具必须去除缩进。我们通过以下惯例实现。第一个行之后的第一个非空行确定整个文本的缩进数目。(我们不能使用第一行,因为它通常与字符串的开头引号相邻,所以它的缩进在文本中并不明显)。所有行的字符串的开头被去除与之相同的空白。缺少缩进的行不应该出现,但是所有行领先的空白应该被去除。应在扩展标签后测试空格的有效性。

>>> def my_function():
...     """Do nothing, but document it.
...
...     No, really, it doesn't do anything.
...     """
...     pass
...
>>> print(my_function.__doc__)
Do nothing, but document it.

    No, really, it doesn't do anything.

功能注释(Function Annotations)

功能注释关于用户定义函数使用的类型的可选元数据信息。

注释作为字典存储在函数的__annotations__属性中,对函数的其他任何部分没有任何影响。功能注释被参数名后面的冒号所定义,后面可以用表达式表示参数值。返回注释有 –> 定义,后面是表达式,在参数列表与表示 def 语句结束的冒号之间。下面的例子有一个位置参数,一个关键字参数,并且返回值被注释了。

def f(ham: str, eggs: str = 'eggs') -> str:
    print("Annotations:", f.__annotations__)
    print("Arguments:", ham, eggs)
    return ham + ' and ' + eggs
f('spam')

间奏曲(intermezzo):代码风格

四个空格在大小缩进得到了平衡(缩进短允许更大的嵌套深度,缩进长更易于阅读),Tabs 会导致误解,最好被省略。

这有助于用户使用小型显示器,并且实现了在较大显示器并排显示代码文件

数据结构

列表

在列表末尾增加元素。等同于 a[len(a):] = [x]

扩展列表通过追加来自可迭代对象中的元素。等同于a[len(a):] = iterable

在给定位置插入元素。第一个参数要插入元素的索引。所以a,insert(0, x)在列表的最前面插入数据,而a.insert(len(a), x)则等同于 a.append(x)

删除列表中第一次出现的值为 x 的数。如果没有这个元素,会返回错误。

删除列表中给定位置的元素,然后返回这个元素。如果没有说明索引,a.pop()会删除并返回列表中的最后一个元素。(方法中 i 周围的方括号说明参数是可选参数,不是你应该在那个位置敲上方括号,你将会看到这个记号在 Python 参考库频繁地出现)

从列表中删除全部的元素,等同于 del a[:]。a[:]相当于是列表a 的浅拷贝,删除a[:]就是删除列表a中所有元素。如果使用 del a,就相当于删除列表a。

返回在以零为列表首元素索引的列表中,值为 x 的元素的索引。如果没有该元素,会产生 ValueError。

可选参数 start 和 end 用于限定搜索的子序列的开始与结束点。被返回的索引是相对于完整序列的开头而不是由 start 参数计算出来的。

返回列表中 x 出现的次数。0:0

给列表中的元素排序。

逆序。

返回 list 的浅拷贝,就相当于 a[:]

你可能注意到了,像insert,remove,sort之类的方法只修改 list 中的元素,不返回打印的值------他们只返回 None。这是 Python 中所有可变数据结构的设计理念。不支持 list.insert(0, 12).remove() 之类的连续操作。

作为栈使用

list 的方法使得我们非常轻松就可以把列表当作栈使用,栈中最后被添加的元素是第一个出栈的元素。在栈的顶部添加元素,使用 append。从栈的顶部取走元素,使用 pop() 方法

作为队列使用

我们也可以将列表当作队列使用,其中先进先出。但是,对于这种情况,列表并不是很高效。尽管在 list 末尾进行的 append 和 pop 操作是快速的,但是在列表的头部插入和删除数据是缓慢的。因为其他的元素要被一个个的移动。

为了补全队列,使用·collections.deque,这种方法被设计快速而高效地从两端删除与添加元素。

from collections import deque
queue = deque([1,2,3])
queue.append(0)
queue.appendleft(8)

列表推导式

列表推导式提供了一种更简洁的方式创建列表。这常用于创建怎样的新列表呢?通常是列表中的元素是对其他序列或者可迭代变量的成员进行一系列操作的结果。或者在一定条件下,创建这些元素的子序列。

squares = [x**2 for x in range(10)]

更加简洁,可读性更强

列表推导式方括号中的表达式后面可以加上 for 语句,然后 0 个或者更多的 for 或者 if 语句。结果将会是一个新列表,通过 for 或者 if 语句限定得到的表达式

[(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]

表达式是元组必须括起来

# flatten
vec = [[1,2,3], [4,5,6], [7,8,9]]
[num for elem in vec for num in elem]

嵌套列表推导式

列表推导式的第一个表达式可以是任意的表达式,包括另一个列表推导式。

[[row[i] for row in matrix] for i in range(4)]
list(zip(*matrix))

del 语句

元组与序列

我们看到列表与字符串有许多共同的性质,比如索引与切片操作。他们都是序列数据类型。由于 Python 是一种不断发展的语言,其他序列数据类型也可能被增加。Python 中存在其他标准序列数据类型:元组。

元组是由逗号隔开的值,是不可改变量(immutable),但是可以包含列表等可变对象。

正如你看到的,输出元组总是被括号包围,所以嵌套元组总会被正确地解释,尽管通常括号是不必要的(如果元组是更大表达式的一部分)。不可能给元组单个元素赋值,但是我们可以创建包含可变对象的元组。

尽管元组看上去与列表类似,但他们通常被用于不同的场合为了不同的目的。元组是不可更改的,通常包含不同类型的序列,这些序列可以通过解包或者索引来获取。列表是可变对象,列表的元素通常是同类型的,可以通过迭代来获取元素。(元组也可以)

一个特殊的问题是包含一个或者零个元素的元组构造:语法有特定的处理来适应这些。空元组由一对空括号组成,只有一个元素的元组,构造为:数值加上逗号(将一个值括起来是不够的)。很丑,但是有效。

解包:t = 1, 2, 3 x, y, z = t

序列解包,适用于右边所有序列。序列解包要求,左边的变量个数要与右边序列元素个数相同。注意多变量赋值只是先将序列打包再解包的过程。

集合

a = {x for x in ‘abracadabra’ if x not in ‘abc’ }

字典

Python 内置的另一个有用的数据类型是字典。字典有时在其他语言中被认为是“associative memories” 或者是“associative arrays”。不像序列可以被一些数字索引,字典是通过关键字索引,关键字可以是任何不可变量类型。字符串与数字总是可以作为关键字。元组可以被用作关键字,如果他们只包含字符串、数字或者元组。如果一个元组直接或者间接地包含任何可变对象,那么他不可以用作关键字。你不能使用列表作为关键字,因为我们可以使用索引赋值、切片赋值或者 append、extend 等方式修改列表元素。

我们最好认为字典是键值对的集合,前提条件是关键字都是互异的。空的大括号创建空子典。在大括号中使用逗号隔开键值对可以初始化字典,这也是字典在输出上的写法。

字典的主要操作是存储一些关键字并且基于关键字提取数值。我们也可以通 del 来删除键值对。如果你对一个已经存在的关键字赋值,那么旧的关键字对应的值将会被覆盖。如果你使用不存在的关键字,程序就会报错。

执行 list(d) 会按原有顺序返回字典中的所有关键字(如果你只想排序的话,使用 sorted(d) 代替)。为了检验单个关键字是否在字典中,我们可以使用 in 这个关键字。

dict() 可以从其他包含键值对的序列构造字典。

dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
dict([['sape', 4139], ['guido', 4127], ['jack', 4098]])

字典推导式也支持任意的键值对表达式

{x: x**2 for x in (2, 4, 6)}

如果关键字是简单字符串,有时使用关键字参数来说明键值对更加方便。

循环技巧

当迭代字典时,我们通过 items() 方法可以同时取出关键字与对应值。

迭代序列,使用 enumerate(),我们可以同时得到位置索引和对应值。

为了同时迭代两个或者更多的序列,我们可以使用 zip() 函数。

为了以相反顺序迭代,我们可以使用 reversed() 函数 a[::-1]

为了按照顺序迭代序列,使用 sorted() 函数。该函数返回一个新的有序序列,并保持源头(被拷贝的序列)不变。

当你迭代一个列表时,你很有可能改变了列表,所以更简单、更安全的方式是再新建一个列表。

上一篇 下一篇

猜你喜欢

热点阅读