python 语言精要

python 语言精要

Python 解释器

Python 是一种解释性语言。Python 解释器是通过“一次执行一条语句”的方式运行程序的。Python 科学计算更趋向于使用 IPython(一种加强的交互式 Python 解释器)。

基础知识

  • Python 是通过空白符(制表符或空格)来组织代码的,不像其他语言(如 R,C++,Java,Perl 等)用的是大括号。同时,强烈建议用 4 个空格作为默认缩进量,这样,你的编辑器就会将制表符替换为 4 个空格。
  • Python 语言的一个重要特点就是其对象模型的一致性。Python 解释器中的任何数值、字符串、数据结构、函数、类、模块等都待在它们自己的“盒子”里,而这个“盒子”也就是 Python 对象。
  • Python 使用 # 进行注释,任何前缀为井号 “ # ” 的文本都会被 Python 解释器忽略掉。
  • 在 Python 中对变量赋值时,其实是在创建等号右侧对象的一个引用。赋值(assignment)操作也叫做绑定(binding),因为其实是将一个名称与一个对象绑定到了一起。已经赋过值的变量名有时也被称为已绑定变量(bound variable)。
a = [1, 2, 3]
b = a
a.append(4)
b # [1, 2, 3, 4]
  • 跟许多编译语言(如 Java 和 C++)相反,Python 中的对象引用没有与之关联的类型信息。变量其实就是对象在特定命名空间中的名称而已。对象的类型信息是保存在它自己内部的。因此,Python 仍然可以被认为是一种强类型语言。可以通过 isinstance() 函数,检车一个对象是否使某个特定类型的实例。
  • Python 提供了 getattr(),hasattr(),setattr() 三个函数实现通过名称对函数的调用,尤其是在编写通用的、可复用的代码时,它们就变得非常实用了。
  • 鸭子模型:“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。” 在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定
  • 引入
import some_module
from some_module import f, g, PI
import some_module as sm
  • 比较运算符:要判断两个引用是否指向同一个对象,可以使用 is 关键字;如果想判断两个引用是否不是指向同一个对象,则可以使用 is not。而 == 则是判断对象中可比较的元素的大小是否相同。is 和 is not 常常用于判断变量是否为 None,因为 None 的实例只有一个。
  • 严格与懒惰:在 Python 中,只要这些语句被求值,相关计算就会立即(也就是严格)发生。而在另一种编程范式中,值在被使用之前是不会被计算出来的。这种将计算推迟的思想通常称为延迟计算(lazy evaluation)。有一些 Python 技术(尤其是用到迭代器和生成器的那些)可以用于实现延迟计算。在数据密集型应用中,但执行一些负荷非常高的计算时(这种情况不太多),这些技术就能派上用场了。
  • 可变和不可变对象:大部分 Python 对象是可变的(muable),比如列表、字典、Numpy数组以及大部分用户自定义类型(类)。也就是说,它们所包含的对象或值是可以被修改的。而其他的(如字符串和元组等)则是不可变的(immutable)。

标量类型

Python 有一些用于处理数值数据、字符串、布尔值(True 或 False)以及日期/时间的内置类型

  • None:Python 的 null 值(None 只存在一个实例对象)
  • str:字符串类型。Python 2.x 中只有 ASCII 值,而 Python 3 中则是 Unicode
  • unicode:Unicode 字符串类型
  • float:双精度(64位)浮点数。注意,这里没有专门的 double 类型
  • bool:True 或 False
  • int:有符号整数,其最大值由平台决定
  • long:任意精度的有符号整数。大的 int 值会被自动转换为 long 

数值类型:Python 3 中整数除法除不尽时就会产生一个浮点数,如果需要得到 C 风格的整数除法(如果除不尽,就丢弃小数部分),使用除后圆整运算符(//)

字符串:编写字符串变量时,既可以用单引号(‘)也可以用双引号(")。对于带有换行符的多行字符串,可以使用三重引号(即 ''' 或 """)。Python 字符串是不可变的,要修改就只能创建一个新的。由于字符串其实是一串字符序列,因此可以被当做某种序列类型(如列表、元组等)进行处理。反斜杠(\)是转义符,也就是说,它可用于指定特殊字符(比如新号 \n 或 unicode 字符)。不过,如果字符串带有很多反斜杠且没有特殊字符,你就发现这个办法很容易让人抓狂。幸运的是,你可以在字符串最左边引号的前面加上 r,它表示所有字符应该按照原本的样子进行解释。同时,Python 3 给出了一个新的字符串格式化手段

template = "%.2f %s are worth $%d"
template % (4.5560, 'Argentine Pesos', 1)

布尔值:几乎所有内置的 Python 类型以及任何定义了 __nonzero__ 魔术方法的类都能在 if 语句中被解释为 True 或 False。Python 中大部分对象都有真假的概念。比如空序列(列表、字典、元组等)用于控制流就会被当做 False 处理。要想知道某个对象究竟会被强制转换成哪个布尔值,可以使用 bool() 函数。

类型转换:str、bool、int 以及 float 等类型也可用作将值转换为该类型的函数

None:None 是 Python 的空值类型。如果一个函数没有显式地返回值,则隐式返回 None。None 还是函数可选参数的一个常见类型。不过,None 不是一个保留关键字,它只是 NoneType 的一个实例而已。

日期和时间:Python 内置的 datetime 模块提供了 datetime、date 以及 time 等类型。datetime 类型是用得最多的,它合并了保存在 date 和 time 中的信息。

from datetime import datetime, date, time
dt = datetime(2011, 10, 29, 20, 30, 21)
dt.day # 29
dt.minute # 30

给定一个 datetime 实例,你可以通过调用其 date 和 time 方法提取相应的 date 和 time 对象。

dt.date() # datetime.date(2011, 10, 29)

dt.time() # datetime.time(20, 30, 21)

dt.strftime('%m/%d/%Y %H:%M') # '10/29/2011 20:30'

datetime.striptime('20191031', '%Y%m%d') # datetime.datetime(2009, 10, 31, 0, 0)

dt.replace(minute=0, second=0) # datetime.datetime(2011, 10, 29, 20, 0)

dt2 = datetime(2011, 11, 15, 22, 30)
delta = dt2 - dt # delta = datetime.timedelta(17, 7179)

dt + delta # datetime.datetime(2011, 11, 15, 22, 30)

异常处理:

f = open(path, 'w')

try:
    write_to_file(f)
except: # or except (IOError, ...):
    print 'Failed'
else:
    print 'Successed'
finally:
    f.close()

range 和 xrange:对于 Python 2,range 产生一组间隔平均的整数,对于非常长的范围,建议使用 xrange,它不会预先产生所有的值并将它们保存到列表中(可能会非常大),而是返回一个用于逐个产生整数的迭代器。不过在 Python 3 中,range 始终返回迭代器,而取消了 xrange。

三元表达式:value = ture-expr if condition else false-expr

数据结构和序列

元组

元组(tuple)是一种一维的、定长的、不可变的 Python 对象序列。调用 tuple 任何序列或迭代器都可以被转换为元组。

nested_tup = (4, 5, 6), (7, 8)

tuple([4, 0, 2]) # (4, 0, 2)

tup = tuple('string') # ('s', 't', ...)
tup[1] # 't'

(4, None, 'foo') + (6, 0) + ('bar',) # (4, None, 'foo', 6, 0, 'bar')

('foo', 'bar') * 2 # ('foo', 'bar', 'foo', 'bar') 这里并没有复制,只是增加了引用而已

tup = 4, 5, (6, 7)
a, b, (c, d) = tup

seq = [(1 ,2, 3), (4, 5, 6)]
for a, b, c in seq:
    pass

列表

跟元组相比,列表(list)是变长的,而且其内容也是可以修改的。它可以通过 [] 或 list 函数进行定义。

tup = ('foo', 'bar', 'baz')
b_list = list(tup)
b_list.append('dwarf')
b_list.insert(1, 'red') # 耗时
b_list.pop(2) # 耗时
b_list.remove('foo')
'dwarf' in b_list

b_list + ['baz'] # 耗时
b_list.extend(['baz'])

a = [7, 2, 5, 1, 3]
a.sort # a = [1, 2, 3, 5, 7]

b.sort(key=len)

import bisect
# bisect 模块实现了二分查找以及对有序列表的插入操作
bisect.bisect(c, 2) # 找出新元素应该被插入到哪个位置才能保持原列表的有序性
bisect.insort(c, 6) # 确实地将新元素插入到那个位置上去

a[1:3:2] # start:stop:step
a[-4:]
a[::-1] # 翻转列表

for i, value in enumerate(collection):
    pass

mapping = dict((v, i) for i, v in enumerate(some_list))

b = sorted(a)

rlt = sorted(set(a)) # 得到一个序列中的唯一元素组成的有序列表

a = [1, 2, 3]
b = [4, 5, 6]
zip_rlt = zip(a, b) # [(1,4), (2,5), (3,6)]

for i, (a, b) in enumerate(zip(seq1, seq2)):
    print('%d,: %s, %s' % (i, a, b))


a, b = unzip(zip_rlt)

list(reversed(range(10))) # [9, 8, 7, ...]

字典

字典(dict)可算是 Python 中最重要的内置数据结构。它更常见的名字是哈希映射(hash map)或相联数组(associative array)。它是一种大小可变的键值对集,其中的键(key)和值(value)都是 Python 对象。

d1 = {'a' : 'some value', 'b' : [1, 2, 3, 4]}

d1[7] = 'an integer' # d1 = {'7' : 'an integer', 'a' : 'some value', 'b' : [1, 2, 3, 4]}

d1['b'] # [1, 2, 3, 4]

'b' in d1 # True

del d1[7] # d1 = {'a' : 'some value', 'b' : [1, 2, 3, 4]}

d1.pop('a') # 'some value'

d1.keys()  # Python 2 返回列表
d1.values() # Python 3 返回迭代器

d1.update({'b' : 'foo', 'c' : 12}) # 使用一个 dict 更新另一个 dict

# 字典本质上就是一个二元元组集,所以完全可以用 dict 类型直接处理二元元组列表
mapping = dict(zip(range(5), reversed(range(5))))

字典的默认值操作

value = some_dict.get(key, default_value)

words = ['apple', 'bat', 'bar', 'atom', 'book']
by_letter = {}
for word in words:
    letter = word[0]
    by_letter.setdefault(letter, []).append(word)

# {a : ['apple', 'atom'], 'b' : ['bat', 'bar', 'book']}

from collections import defaultdict
by_letter = defaultdict(list)
for word in words:
    by_letter[word[0]].append(word)

counts = defaultdict(lambda: 4) # 默认值为 4

虽然字典的值可以是任何的 Python 对象,但键必须是不可变对象,如标量类型(整数、浮点数、字符串)或元组(元组中的所有对象也必须是不可变的)。这里的术语是可哈希性(hashability)。通过 hash() 函数,可以判断某个对象是否是可哈希的(即可以用作字典的键)。如果要将列表当做键,最简单的方法就是将其转换为元组。

集合

集合(set)是由唯一元素组成的无序集。你可以将其看成是只有键值而没有值的字典。

# 创建字典
set([1, 2, 3])
{1, 2, 3}

# 集合运算
a | b # 并
a & b # 交
a - b # 差
a ^ b # 异或

a.add(x) # 增加元素
a.remove(x) # 移除元素
a.union(b) # a | b
a.intersection(b) # a & b
a.difference(b) # a - b
a.symmetric_difference(b) # a ^ b
a.issubset(b) # 是否 a 包含于 b
a.issuperset(b) # 是否 a 包含 b
a.isdisjoint(b) # 是否相交

列表、集合以及字典的推导式

list_comp = [expr for val in collection if condition]

dict_comp = {key-expr : value-expr for value in collection if condition}

set_comp = {expr for value in collection if condition}

loc_mapping = {val : index for index, val in enumerate(strings)}
loc_mapping = dict(val : index for index, val in enumerate(strings))

flatted = [x for tup in some_tuples for x in tup]
[[x for x in tup] for tup in some_tuples] # 注意两者的差异

函数

函数可以有一些位置参数和一些关键值参数。关键字参数通过用于指定默认值或可选参数。函数参数的主要限制在于:关键字参数必须位于位置参数(如果有的话)之后。你可以任何顺序指定关键值参数。也就是说你不用死记硬背函数参数的顺序,只要记得它们的名字就可以了。

命名空间、作用域,以及局部函数

函数可以访问两种不同作用域中的变量:全局(global)和局部(local)。Python 有一种更科学的用于描述变量作用域的名称,即命名空间(namespace)。任何在函数中赋值的变量默认都是被分配到局部命名空间(local namespace)中的。局部命名空间是在函数被调用时创建的,函数参数会立即填入该命名空间。在函数执行完毕之后,局部命名空间就会被销毁(会有一些例外的情况,比如闭包)。虽然可以在函数中对全局变量进行赋值操作,但那些变量必须用 global 关键字声明成全局的才行。

可以在任何位置进行函数声明,即使是局部函数(在外层函数被调用之后才会被动态创建出来)也是可以的。各个嵌套的内层函数可以访问其上层函数的局部命名空间,但不能绑定新变量。

严格意义上说,所有函数都是某个作用域的局部函数,这个作用域可能刚好就是模块级的作用域。

返回多个值

Python 支持函数的返回多个值。本质上,函数其实只返回了一个对象,也就是一个元组,最后该元组会被拆包到各个结果变量中。如果直接使用一个变量接受一个返回多个变量的函数结果,那么这个变量将是一个包含所有返回的元组。

函数也是对象

由于 Python 函数都是对象,因此,在其他语言中较难表达的一些设计思想在 Python 中就要简单很多了。

# 实现去除空白符,删除各种标点符号,正确的大写格式等
import re
def remove_punctuation(value):
    return re.sub('[!#?]', '', value)

clean_ops = [str.strip, remove_punctuation, str.title]

def clean_strings(strings, ops):
    result = []
    for value in strings:
        for function in ops:
            value = function(value)
        result.append(value)
    return result

# map(remove_punctuation, states)

匿名(lambda)函数

Python 有一种被称为匿名函数或 lambda 函数的东西,这其实是一种非常简单的函数:仅由单条语句组成,该语句的结果就是返回值。它们通过 lambda 关键字定义,这个关键字没有别的含义,仅仅是说“我们正在声明的是一个匿名函数”。

def short_function(x):
    return x ** 2

equiv_anon = lambda x: x ** 2

闭包:返回函数的函数

闭包(closure)不是什么很可怕的东西。简而言之,闭包就是由其他函数动态生成并返回的函数。其关键性质是,被返回的函数可以访问其创建者的局部命名空间中的变量。闭包和标准 Python 函数之间的区别在于:即使其创建者已经执行完毕,闭包仍然能继续访问其创建者的局部命名空间。虽然闭包的内部状态一般都是静态的,但也允许使用可变对象(如字典、集合、列表等可以被修改的对象)。但是要注意一个技术限制:虽然可以修改任何内部状态对象(比如向字典添加键值对),但不能绑定外层函数作用域中的变量。一个解决方法是:修改字典和列表,而不是绑定变量。

def format_and_pad(template, space):
    def formatter(x):
        return (template % x).rjust(space)

    return formatter

fmt = format_and_pad("%.4f", 15) # 创建一个始终返回 15 位字符串的浮点数
fmt(1.756)

扩展调用语法和 *args 、**kwargs

Python 函数参数的工作方式其实很简单。位置和关键字参数其实分别是被打包成元组和字典的。函数实际收到的是一个元组 args 和一个字典 kwargs,并在内部完成转换。

func(a, b, c, d=some, e=value)

a, b, c = args
d = kwargs.get('d', some)
e = kwargs.get('e', value)

柯里化:部分参数应用

柯里化(currying)指的是通过“部分参数应用”从现有函数派生出新函数的技术。

def add_numbers(x, y):
    return x + y

add_five = lambda y: add_numbers(5, y)

from functools import partial
add_five = partial(add_numbers, 5)

# 计算时间序列 x 的 60 日移动平均
ma60 = lambda x: pandas.rolling_mean(x, 60)
data.apply(ma60)

生成器

能以一种一致的方式对序列进行迭代(比如列表中的对象或文件中的行)是 Python 的一种重要特点。这是通过一种叫做迭代器协议的方式实现的。当调用 for xx in xxx 时,Python 解释器首先会尝试从 xxx 创建一个迭代器。迭代器是一个特殊对象,他可以在诸如 for 循环之类的上下文中向 Python 解释器输送对象 。大部分能接受列表之类的对象的方法也都可以接受任何可迭代对象。生成器(generator)是构造新的可迭代对象的一种简单方式。一般的函数执行之后只会返回单个值,而生成器则是以延迟的方式返回一个值的序列,即每返回一个值之后暂停,直到下一个值被请求时再继续。要创建一个生成器,只需将函数中的 return 替换为 yield 即可。 

def squares(n=10):
    for i in range(1, n+1):
        print 'Generating squares from 1 to %d' % (n ** 2)
        yield i ** 2

生成器表达式

gen = (x ** 2 for x in range(100))

itertools 模块

标准库 itertools 模块中有一组用于许多常见数据算法的生成器。

  • imap
  • ifilter
  • combinations
  • permutations
  • groupby:根据函数返回值对序列中的连续元素进行分组
import itertools

first_letter = lambda x: x[0]

names = ['Alans', 'Adam', 'Wes', 'Will', 'Albert', 'Steven']

for letter, names in itertools.grouby(name, first_letter)

文件和操作系统

为了打开一个文件以便读写,可以使用内置的 open 函数以及一个相对或绝对的文件路径。默认情况下,文件是以只读模式(’r‘)打开的。然后,就可以想处理列表那样来处理这个文件句柄了。从文件中读出的行都带有完整的行结束符(EOF)。

lines = [x.rstrip() for x in open(path)]

文件模式: 

  • r:只读模式
  • w:只写模式。创建新文件(删除同名的任何文件)
  • a:附加到现有文件(如果文件不存在则创建一个)
  • r+:读写模式
  • b:附加说明某模式用于二进制文件,如 'rb' 或 'wb'
  • U:通用换行模型。单独使用或附加到其他模式,如 'rU'

常用方法:

  • read([size]):以字符串形式返回文件数据,可选的 size 参数用于说明读取的字节数
  • readlines([size]):将文件返回为行列表
  • write(str):将字符串写入文件
  • close():关闭句柄
  • flush():清空内部 I/O 缓存区,并将数据强行写回磁盘
  • seek(pos):将文件指针移动到指定的文件位置(整数)
  • tell():以整数形式返回当前文件指针位置
  • closed:如果文件已关闭,则为 True
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值