记得我是数学系的,大二时候因为加入数学建模,学习 Python 爬虫,去图书馆借了一本 Python 根底书,不厚,因为有
matlab
和C 语言
根底,这本书一个星期看完了,学完后感觉 Python 入门很快,而后要开始学爬虫和矩阵计算,学习一下对应的包就行了,感觉很不便,爱上了这门语言,尽管毕业后做了 Java,然而平时工作中也会用一些 Python。不过初学者有很多根底知识点记不住,因为用得少,这里总结记录一下。更多 Python 常识,能够返回我的集体博客网站 期待下一个秋 -Python
介绍
Python 是一门独特的语言,疾速浏览一下他的要点:
- 面向对象:每一个变量都是一个类,有其本人的属性(attribute)与办法(method)。
- 语法块:用缩进(四个空格)而不是分号、花括号等符号来标记。因而,行首的空格不能随便书写。
- 正文:行内用“#”号,行间正文写在两组间断三单引号之间:’’’
- 续行:行尾输出一个反斜杠加一个空格(’\‘),再换行。如果行尾语法显著未实现(比方以逗号结尾),能够间接续行。
- 打印与输出:函数 print() 与 input(),留神 print() 的 sep 与 end 参数。
-
变量:无需指定变量类型,也不须要提前申明变量。
- 删除变量:del()
- 复制变量:间接将变量 a 赋值给 b,有时仅仅复制了一个“援用”。尔后 b 与 a 的改变仍会相互影响。必要时应用 a is b 来判断是否同址。
- 模块:通过 import pandas 的形式加载模块(或者 import pandas as pd),并用形如 pandas.DataFrame(或 pd.DataFrame)的形式调用模块内的办法。也能够应用 from pandas import DataFrame 的形式,这样在下文能够间接应用 DataFrame 作为调用名。
- 帮忙:配合应用 dir() 与 help() 命令;其中前者是输入变量所有的成员。以及查阅 官网页面。
变量复制的一个例子。
a = [1, 2]
b = a
print(id(a) - id(b)) # 地址差为 0,示意本质是同址的
0
b.append(3)
print(a) # 只改变了 b,但 a 也跟着变动了
[1, 2, 3]
a is b
True
应用切片来重新分配空间:
a is a[:]
False
数据结构
Python 原生的数据结构包含:
数字(num)
细分为整数(int)与浮点数(float)两种。
- 四则运算:+,-,,/,乘方:*
- 整除:5 // 2 = 2,取余:5 % 2 = 1
- 自运算:a += 1(四则与乘方均可相似自运算)
以及一些细节: - 运算两数中只有有一个浮点数,后果就是浮点数;
- 整数相除,即便能除尽,后果也是浮点数;
- Python 外部的机制解决了整数溢出的问题,不必放心。
布尔(bool)与逻辑
首字母大写 True / False.
- 逻辑运算符:与 A and B,或 A or B,非 not A
- 逻辑关系符:等于 ==,不等于 !=. 其余不赘述。
- 几种逻辑判断例子:
变量 x | x = [] | x = 0 | x = 2 |
---|---|---|---|
bool(x) | False | False | True |
if x: … | False | False | True |
if x is None: … | False | False | False |
序列(sequence)
序列次要包含 字符串(str)、列表(list)与元祖(tuple)三类。
-
序列索引规定:
- 索引从 0 开始,到 N-1 完结。
-
切片:切片的索引是左闭右开的。
- seq[0:2](从 0 到 1)
- seq[2:](从 2 到尾)
- seq[:3](从头到 2)
- seq[:](全副)
- seq[:10:2](从头到 9,每两个取一个)
- seq[::2](全副,每两个取一个)
- 索引容许正数:seq(-1) 与 seq(N – 1) 等同,seq(-3:-1)与 seq(N-3:N-1) 等同。
-
序列通用函数:
- len():返回序列长度。
- +/*:加号用于连贯两个序列,乘号反复排列若干次再连贯。
- seq1 in seq2:如果 seq1 这个片段能够在 seq2 中被找到,返回 True.
- index:在 seq1 in seq2 为 True 时应用,seq2.index(seq1) 示意 seq1 首次呈现于 seq2 中的地位。
- max()/min():返回序列中的最值。如果不是数字,则按 ASCII 码程序返回。
- cmp(seq1, seq2):比拟大小。后果为负,则示意 seq1 较小。
字符串(str)
写于一对双引号或单引号内。用 str() 能够强制转换为字符串。
- 本义:反斜杠。如果强制不解释字符串,在左引号前加字母 r 即可:
r"c:\new"
. -
宰割与连贯:
**.split()
与**.join()
.s = "I love Python" # 首位是空格 lst = s.split(' ') lst1 = '-'.join(lst) print(lst, '\n', lst1) ['','I','love','Python'] -I-love-Python
-
紧切:
strip()
去掉字符串首尾两端的空格。办法lstrip()/rstrip()
则只切除首端 / 尾端的空格。s.strip() 'I love Python'
-
大小写转换:如下几个办法:
- 首字母大写:s.title()
- 全大写:s.upper()
- 全小写:s.lower()
- 句首大写:s.capitalize()
-
格式化:字符串格式化是一种实用功能。通过 .format() 成员函数实现。
'I like {} and {}'.format('Python', 'you') 'I like Python and you' '{0} + {2} = {1}'.format (10, 20, 'Python') # 按程序援用 '10 + Python = 20' '{0} * {1} = {0}'.format (10, 'Python') # 编号重复援用 '10 * Python = 10'
格式化管制码:
管制码 | 含意 | 管制码 | 含意 |
---|---|---|---|
:s | 字符串 | :c | 单个字符 |
:b/o/x/d | 二、八、十六、十进制数 | :e/f | 迷信计数法 / 浮点数 |
一些简单管制的例子:
例子 | 含意 | 例子 | 含意 |
---|---|---|---|
:.2f/:+.2f | 两位小数 / 带符号两位小数 | : .2f | 负数前补空格的两位小数 |
:, | 逗号分隔符 | :.2% | 百分比两位小数 |
:.2e | 迷信计数法两位小数 | :^4d | 总宽四位居中对齐 |
:>4d/<4d | 总宽四位左 / 右对齐 | :0>4d | 总宽四位左侧补零 |
举例:
"{:0>7.2f} is an odd number".format(123.4) # 总宽 7 位小数点后 2 位,左侧补零
'0123.40 is an odd number'
其余实用的字符串函数:
- str.replace(old, new[, times]):将字符串中前 times 个 old 子串替换为 new。Times 不指定时默认替换全副。
- str.isdigit():判断字符串是否每一位都是数字,返回 True 或者 False。
字符串中正则表达式的内容参见本文附录。
列表(list)
中括号式的构造。list()
用于强制转换类型。
lst = [1, 2, 3]
print(lst)
[1, 2, 3]
#【反转】:其中第二种形式会更改现有的列表
lst1 = list(reversed(lst))
lst.reverse()
print(lst1, lst)
[3, 2, 1] [3, 2, 1]
#【追加】:元素 append(),另一个列表:extend()
lst.append(4)
print(lst)
[3, 2, 1, 4]
lst.extend(lst1)
print(lst)
[3, 2, 1, 4, 3, 2, 1]
#【插入】:lst.insert(idx, obj) 会在 lst[idx] 处插入 obj,而后顺次后移原有项
lst.insert(1, 100)
print(lst)
[3, 100, 2, 1, 4, 3, 2, 1]
#【删除】:lst.remove(obj) 会删除首个匹配值,若无匹配会报错;# lst.pop(idx) 会返回 lst[idx],并将其删除。如果不指定 idx,默认为列表尾
lst.remove(2)
print(lst)
[3, 100, 1, 4, 3, 2, 1]
tmp = lst.pop()
print(lst, "\n", tmp)
[3, 100, 1, 4, 3, 2]
1
#【搜寻】:应用序列通用函数即可。用 count(obj) 能够计算频数。#【排序】:sort() 办法。如果指定 reverse 参数,可降序排序。lst.sort(reverse=True)
print(lst)
[100, 4, 3, 3, 2, 1]
#【清空】:clear()
lst.clear()
print(lst)
[]
元组(tuple)
圆括号式的构造,是一种不可变序列。
a = (1, 'string', [1 ,2])
print(a)
(1, 'string', [1, 2])
Note: 定义一个空的元组用()
,定义只有一个元组的元组,须要加,
,否则就不是元组了,如下:
>>> tuple1 = ()
>>> type(tuple1)
<type 'tuple'>
>>> tuple2 = (1)
>>> type(tuple2)
<type 'int'>
>>> tuple3 = (1,)
>>> type(tuple3)
<type 'tuple'>
字典(dict)
字典是一品种哈希表的数据结构,外部无序,通过键值对(key: value)的模式存储数据。几种字典初始化的形式:
# 小字典间接赋值
d1 = {"name": "wklchris", "gender": "male"}
# 利用字典减少键值对的办法
d2 = {}
d2['name'] = 'wklchris'
# 一个值赋给多个键
d3 = {}.fromkeys(("name", "gender"), "NA")
# 强制格局转换
d4 = dict(name="wklchris", gender="male")
print(d1, d2, d3, d4, sep="\n")
{'name': 'wklchris', 'gender': 'male'}
{'name': 'wklchris'}
{'name': 'NA', 'gender': 'NA'}
{'name': 'wklchris', 'gender': 'male'}
字典的操作方法:
len(d1)
2
#【复制】:dd = d1.copy()
dd is d1
False
#【查找键名称】:"name" in dd
True
#【删除键值对】del(dd["name"])
#【get】dd.get("name", "Nothing") # 如果键不存在,返回“Nothing”'Nothing'
#【setdefault】dd.setdefault("name", "wklchris") # 如果键不存在,就新建该键,并赋值
'wklchris'
print(dd)
{'name': 'wklchris', 'gender': 'male'}
#【输入键值】:list(dd.items())
[('name', 'wklchris'), ('gender', 'male')]
list(dd.keys())
['name', 'gender']
list(dd.values())
['wklchris', 'male']
#【弹出键值对】:pop(key) / popitem(key)
# 其中,后者会随机弹出一个键值对
tmp = dd.pop("gender")
print(dd, tmp)
{'name': 'wklchris'} male
#【更新】:update(ref_dict) 以 ref_dict 为准,更新以后字典
d4 = {"name": "Test", "Age": 3}
dd.update(d4)
print(dd)
{'name': 'Test', 'Age': 3}
汇合(set)
本文只探讨可变汇合,对于不可变汇合的内容,参考 help(frozenset)。
汇合是一种无序的数据存储形式,且外部元素具备唯一性。汇合与字典一样都能够用花括号的模式创建。但在书写 a={} 时,Python 会将其辨认为字典类型。
- 削减:add() / update()
- 删除:remove() / discard(),区别在于后者搜寻无后果会报错。
- 隶属:a.issubset(b) 汇合 a 是否是 b 的子集;a.issuperset(b) 汇合 a 是否是 b 的父集。a == b 两汇合是否全等。
-
汇合运算:汇合运算不会扭转参加运算的汇合自身。
- 并集:a | b 或者 a.union(b)
- 交加:a & b 或者 a.intersection(b)
-
补集:a – b 或者 a.difference(b)
留神:在字符串强制转换为汇合时,必要时应用中括号先转为列表(否则字符串会被拆分为单个字符后再进行转换)。例如:ss = {"a", "b", "c"} ss | set("de") {'a', 'b', 'c', 'd', 'e'} ss | set(["de"]) {'a', 'b', 'c', 'de'}
根本语句
同大多数程序语言一样,Python 领有 if, for, while
语句。什么?switch
语句?应用字典就好。
if 语句与三元操作
在 Python 中,else if
被缩写为单个关键词 elif
.
if 1.0 > 1:
a = 1
elif 1.0 < 1:
a = 2
else:
a = 3
a
3
值得一提的是,Python 中的 if 语句反对链式比拟,形如 a < x < b, a < x >= b
等:
a = 0
if 1 < 2 > 1.5:
a = 1
a
1
三元操作本质是高度简化的 if 环境,形如 X = a if flag else b
:
a = 1 if 2 < 1 else 2
a
2
for 语句
Python 的循环语句中,像其余语言一样,有 break
(跳出循环体)与 continue
(循环步进)关键词能够应用。
for 语句借助关键词 in 应用:(函数 range(N, M=0, s=1)
是一个生成等差数列的函数,位于左闭右开区间 [M,N)
上且公差为 s)。
for i in range(3):
print(i)
0
1
2
留神到字典的 d.items(), d.keys(), d.values() 命令也经常用于 for 语句:
d = {"a": 1, "b": 2, "c": 3}
for k, v in d.items():
print(k, v)
b 2
c 3
a 1
以上等价于:
for k in d.keys():
print(k, d[k])
b 2
c 3
a 1
Python 中的 for
语句可选 else
语法块,示意 for
语句失常完结后执行的内容(中途 break
不属于失常完结)。这对于解决一些 break 操作很有帮忙。例如:
a = 0
flag = 0
for i in range(5):
if i > 2:
flag = 1
break
if flag == 1:
a = 1
a
1
这在 Python 中显得太简单了,间接应用 for…else…
即可:
a = 1
for i in range(5):
if i > 1:
break
else:
a = 0
a
1
while 语句
while 语句的 else
语法块,指明了退出 while 循环后立即执行的内容;它不是必须的。
如果你想要将 while 语句外部的参数传出(比方下例的计数器终值),这是一个不错的计划。
count = 1
while count < 5:
a = count
count *= 2
else:
b = count
print(a, b)
4 8
列表解析
列表解析是一种创立列表的高度缩写形式:
lst = [x ** 2 for x in range(4)]
lst
[0, 1, 4, 9]
也能够配合 if 语句:
lst = [x ** 2 for x in range(4) if x > 0]
lst
[1, 4, 9]
相似的,也有字典解析,以及下文会介绍的生成器,也有生成器解析(把外围的括号换成圆括号即可):
{n: n ** 2 for n in range(3)}
{0: 0, 1: 1, 2: 4}
函数
本节介绍 Python 函数的根底特点,以及一些实用函数。
函数定义与判断
应用 def
关键字。三连双引号间的内容被视为函数的帮忙字符串,能够通过 help()
命令查看。
def func(a, b=0):
"""This is a function that can meow."""
return "".join(["meow"] * (a + b))
调用函数:
func(2) # 单参数,仅 a
'meow meow'
func(2, 3) # 双参数,a 与 b 都被传入
'meow meow meow meow meow'
help(func)
Help on function func in module __main__:
func(a, b=0)
This is a function that can meow.
通过 callable()
能够判断一个对象是否是一个可调用的函数:
callable(func)
True
不定参函数
利用序列(或元组)与字典,向函数传参。前者在传入时须要加上一个星号,后者须要两个。
lst = [1, 3, 4]
d = {"a": 2, "b": 3, "c": 5}
print("{}+{}={}".format(*lst), "{a}+{b}={c}".format(**d))
1+3=4 2+3=5
zip 函数
zip() 函数的作用是“合并”多个列表为一个。其返回值是一个列表,列表内的元素类型是元组。如果待合并的列表长度不同,以最短的为准。
a = [1, 2, 3, 4]
b = [5 ,6, 7]
c = "abcd"
list(zip(a, b, c))
[(1, 5, 'a'), (2, 6, 'b'), (3, 7, 'c')]
它比拟罕用于替换字典的键与值:
dict(zip(d.values(), d.keys()))
{2: 'a', 3: 'b', 5: 'c'}
lambda 函数
一种匿名函数的申明形式。如果你应用过 MATLAB
,你可能相熟这一类概念。
func = lambda x, y: x + y
func(2, 5)
7
map 函数
map()
可能对传入的序列进行顺次操作,并将后果返回为一个可转换为列表的 map
对象。通常列表解析(或生成器解析)能够实现与其同样的工作。
lst = list(map(lambda x: x + 1, range (5)))
print(lst)
[1, 2, 3, 4, 5]
f = lambda x: x + 1
[f(x) for x in range(5)]
[1, 2, 3, 4, 5]
filter 函数
给定序列,对于满足某规定的局部(即 True),予以返回。
list(filter(lambda x: x > 0, range(-3, 3)))
[1, 2]
reduce 函数
该函数在 Python 2 中是能够间接调用的,但在 Python 3 中须要从 functools
模块进行调用。
from functools import reduce
reduce(lambda x, y: x + y, range (5)) # 0+1+2+3+4
10
enumerate 函数
它容许你像 d.items() 那样,用相似的形式操作列表:
a = [1, 3, 5]
for i, v in enumerate(a):
print("lst[{}] = {}".format(i, v))
lst[0] = 1
lst[1] = 3
lst[2] = 5
装璜器:算子
装璜器是函数的函数——传入的参数是一个函数,返回的值也是一个函数。相当于一个函数集到另一个函数集的映射,能够了解为数学意义上的算子。
首先来看一个简略的例子:函数能够被赋值给一个变量。
def pyrint(data="Python"):
return data.upper()
f = pyrint
f()
'PYTHON'
还能够通过 __name__
来失去以后函数的名称:
f.__name__
'pyrint'
那什么时候须要装璜器呢?比方在函数须要被重用、但又不能间接改写 def
的场合(在保护中应该不少见吧!)。例如,咱们心愿在返回值之前,把函数名也打印进去:
def showname(func):
def subfunc(*args, **kwarg):
print("FUNCTION {} called.".format(func.__name__))
return func(*args, **kwarg)
return subfunc
这样如果咱们通过 showname(pyrint)
这种模式,就可能在 pyrint
函数被调用之前,额定打印一行内容。
想要改变该函数,不须要改变 def 语句以下的内容,只须要用 @showname
命令来利用这个装璜器:
@showname
def pyrint(data="Python"):
return data.upper()
pyrint()
FUNCTION pyrint called.
'PYTHON'
如果装璜器须要传递参数,那么,须要在定义时,外层再嵌套一个函数:
def showname(num=1):
def decorator(func):
def subfunc(*args, **kwarg):
print("Call time: {}. FUNCTION {} called.".format(num, func.__name__))
return func(*args, **kwarg)
return subfunc
return decorator
@showname(2)
def pyrint(data="Python"):
return data.upper()
pyrint()
Call time: 2. FUNCTION pyrint called.
'PYTHON'
不过装璜器被利用于函数定义之前时,函数的 __name__
属性会扭转。比方上例:
pyrint.__name__
'subfunc'
应用模块 functools 来解决这一问题:import functools
def showname(num=1):
def decorator(func):
@functools.wraps(func) # 加上这一行
def subfunc(*args, **kwarg):
print("Call time: {}. FUNCTION {} called.".format(num, func.__name__))
return func(*args, **kwarg)
return subfunc
return decorator
@showname(2)
def pyrint(data="Python"):
return data.upper()
pyrint.__name__
'pyrint'
迭代器 [itertools]
迭代器与生成器在内存优化上很有意义。
迭代器
迭代器最显著的特色是领有 __iter__()
和 __next__()
办法;它像一个链表。如果它指向开端,那么再次执行 __next__()
时会报错。一个例子:
a = [1, 2, 3]
b = iter(a)
print(b.__next__(), b.__next__()) # 或者应用 next(b)
1 2
实际上,Python 3 内置了一个 itertools
的库,外面有诸如 cycle
和 count
等实用于迭代器的函数:
import itertools
# count: 给定首项与公差的无穷等差数列
p = itertools.count(start = 1, step = 0.5)
print(p.__next__(), p.__next__())
# cycle: 周期循环的无穷序列
p = itertools.cycle(list("AB"))
print(next(p), next(p), next(p))
# islice: 从无穷序列中切片
p = itertools.cycle(list("AB"))
print(list(itertools.islice(p, 0, 4)))
1 1.5
A B A
['A', 'B', 'A', 'B']
请时刻留神以后指向的迭代器地位——失之毫厘,谬以千里。
生成器
生成器是迭代器的一种,其实质是定义中含有 yield 关键词的函数。它没有 return() 语句。
生成器能够间接应用相似列表解析的形式,称为生成器解析。例如:(i for i in range(10)。
def Fib(N): # 斐波那契数列
n, former, later = 0, 0, 1
while n < N:
yield later
former, later = later, later + former
n += 1
list(Fib(5))
[1, 1, 2, 3, 5]
上例与一般的写法看上去差异不大,但实际上能够将 while 语句改写为 while True
,删除变量 n,在内部借助 itertools 的 islice
函数来截取。这在函数定义时对代码的压缩是显然的。
def iterFib():
former, later = 0, 1
while True:
yield later
former, later = later, later + former
list(itertools.islice(iterFib(), 0, 5))
[1, 1, 2, 3, 5]
谬误:try()
语句
常见的谬误有以下几种:
- ZeroDivisionError: 除数为 0.
- SyntaxError:语法错误。
- IndexError:索引超界。
- KeyError:字典键不存在。
-
IOError:读写谬误。
try() 语句的常见写法:try: a = 1 except ZeroDivisionError as e: print(e) exit() else: # 如果无谬误,执行 print(a) finally: # 不论有无谬误均执行 print("-- End --") 1 -- End --
其中,
else
与finally
语句都不是必须的。如果不想输入错误信息、或不能预先判断可能的谬误类型,能够应用仅含 exit() 语句的except
块。
多个 except 块
一个 try 语法块是能够跟着多个 except
的;如果靠前的 except 捕捉了谬误,之后的就不会运行。这也就是说,如果谬误之间有继承关系时,子谬误须要放在父谬误之前尝试 except,否则子谬误永远也不可能被捕捉。
比方上一节的例子中,ZeroDivisionError
是 ArithmeticError
下的子谬误,而 ArithmeticError
又是 Exception 下的子谬误(当不分明谬误的类型时,Exception 能够捕捉绝大多数谬误)。对于谬误的继承关系,参考:Python – Exception Hierarchy 官网页面。
一个例子:
try:
a = 1 / 0
except Exception:
print("Exception")
exit()
except ZeroDivisionError:
print("ZeroDivisionError")
exit()
else:
print("No error.")
finally:
print("-- End --")
输入 Exception 与 – End –。
谬误的捕捉
谬误在很多中央都可能产生,那是否须要在可能的中央都加上 try 语句呢?当然不是。倡议只在主代码中退出 try 语句,因为 Python 会主动跟踪到谬误产生的源头何在。
谬误的抛出及上抛
有时候咱们想人为抛出一个谬误,这是应用 raise
即可:
# raise TypeError("Wrong type.")
如果在函数中没有处理错误的语句,可能在捕捉谬误后将其上抛。记住,捕捉谬误只是为了记录谬误的产生,并不象征者必须原地解决谬误。
def makeerror(n):
if n == 0:
raise ValueError("Divided by zero.")
return 1 / n
def callerror():
try:
makeerror(0)
except ValueError as e:
print("ValueError detected.")
raise
# 输入 "ValueError detected." 并打印谬误日志
# callerror()
下面的 raise
命令没有紧跟任何参数,示意将谬误原样上抛。你也能够手动指定上抛的谬误类型,并不需要与原谬误类型统一。甚至你能够定义一个谬误(继承某一谬误类):
class MyError(ValueError):
print("This is MyError.")
# raise MyError
This is MyError.
文件读写
open() 函数用于文件的读写操作。个别咱们会在操作文件时,引入 os 模块(os 模块的用法参考“罕用模块”一节的内容)。
import os
open()
函数经常配合 with
语法块进行应用,它会在语法块完结时主动敞开文件。该函数:
open(file, mode="r", encoding=None)
第一参数是蕴含文件名的门路(传入基于当前目录的相对路径,传入或者绝对路径),mode 参数是读写操作形式;encoding
是编码类型,个别取”utf8”
。其中,读写操作形式罕用的有:
参数 | 含意 |
---|---|
“r” | (默认)读。 |
“w” | 写。该模式会笼罩原有内容;如文件不存在,会主动新建。 |
“x” | 创立新文件并写入。 |
“a” | 在已有文件的尾部追加。 |
个别读写操作:read() / readlines()
函数 read()
将整个文件读为一个字符串,来看一个例子:
datapath = os.path.join(os.getcwd(), "data", "iris.data.csv")
with open(datapath, "r", encoding="utf8") as f:
rawtext = f.read()
rawtext[:200]
'5.1,3.5,1.4,0.2,Iris-setosa\n4.9,3.0,1.4,0.2,Iris-setosa\n4.7,3.2,1.3,0.2,Iris-setosa\n4.6,3.1,1.5,0.2,Iris-setosa\n5.0,3.6,1.4,0.2,Iris-setosa\n5.4,3.9,1.7,0.4,Iris-setosa\n4.6,3.4,1.4,0.3,Iris-setosa\n5.0,'
函数 readlines()
将整个文件读为一个列表,文件的每一行对应列表的一个元素。
with open(datapath, "r", encoding="utf8") as f:
rawtext = f.readlines()
rawtext[:3]
['5.1,3.5,1.4,0.2,Iris-setosa\n',
'4.9,3.0,1.4,0.2,Iris-setosa\n',
'4.7,3.2,1.3,0.2,Iris-setosa\n']
上述的 readlines()
函数本质等同于列表解析:
with open(datapath, "r", encoding="utf8") as f:
rawtext = [line for line in f]
rawtext[:3]
['5.1,3.5,1.4,0.2,Iris-setosa\n',
'4.9,3.0,1.4,0.2,Iris-setosa\n',
'4.7,3.2,1.3,0.2,Iris-setosa\n']
文件写入,应用 write()
函数。一个简略的例子:
with open(datapath, "w") as f:
f.write("Sometimes naive.")
大文件读取:readline()
如果文件比拟大,应用 read()/readlines()
函数间接读入可能会占用太多内存。举荐应用函数 readline()
,一种迭代器式的读取办法。
with open(datapath, "r", encoding="utf8") as f:
print(f.readline().strip())
print(f.readline().strip())
5.1,3.5,1.4,0.2,Iris-setosa
4.9,3.0,1.4,0.2,Iris-setosa
你会发现两次后果是不同的,这是因为迭代器外部的“指针”向后挪动了。
怎么获取 / 挪动“指针”的地位呢?应用 tell() / seek() 命令。
with open(datapath, "r", encoding="utf8") as f:
print(f.tell(), f.readline().strip())
print(f.tell(), f.readline().strip())
f.seek(0) # 回到文件头
print(f.tell(), f.readline().strip())
0 5.1,3.5,1.4,0.2,Iris-setosa
28 4.9,3.0,1.4,0.2,Iris-setosa
0 5.1,3.5,1.4,0.2,Iris-setosa
类
类的成员包含属性(attribute
)与办法(method
)两种。例子:
class MyClass:
"""This is a class that can meow!"""
animal = "cat" # An attribute
def talk(self): # A method
return "Meow"
# An instance of the class
a = MyClass()
print(a.animal, a.talk())
cat Meow
上例中的 self
示意类的实例,所有类外部的办法都须要把该参数放在首位(你也不可不必 self 而应用 this 等,然而 self
是常规)。例如,self.animal
就示意了实例的 animal 属性。这与 C# 等语言中的“this.animal”是相似的。
下例证实了 self
代表的本质是类的实例,而不是类自身。
class EgClass:
def __init__(self):
print(self) # 实例,有对应地址
print(self.__class__) # 类
a = EgClass()
<__main__.EgClass object at 0x000002531C0AF860>
<class '__main__.EgClass'>
构造函数:__init__()
类的构造函数是 __init__()
(左右均为双下划线),用于初始化实例。在申明实例时,该函数主动被调用。
class MyClass2:
def __init__(self, animal="cat"):
self.animal = animal
a = MyClass2("dog")
a.animal
'dog'
封装
类的重要个性是封装性,即局部变量只能在其外部批改或拜访,不能从类的内部进行解决。Python 中的封装非常简单,只有把属性或办法的名称前缀设置为双下划线即可。
由此可见,构造函数 __init__()
是最根本的一个公有办法。一个例子:
class MyClass3:
def __init__(self, animal="cat"):
self.__animal = animal
self.__foo()
def __foo(self):
self.__animal = "rabbit"
def show(self):
print(self.__animal)
a = MyClass3("dog")
a.show()
rabbit
如果想间接调用 __foo() 或者 __animal
,都会被禁止,产生 AttributeError
。
# a.__animal # AttributeError
要留神,前后均增加了双下划线的属性,如 name,示意非凡属性而不是公有属性,是能够从内部拜访的。
继承
上面是一个驰名的猫与狗的例子;类 Cat 与 Dog 都继承自 Animal,同时也都重载了办法 talk()。
class Animal:
def talk(self):
pass # 示意定义留空
class Cat(Animal): # 从 Animal 继承
def talk(self): # 重写 talk()
print('Meow')
class Dog(Animal):
def talk(self):
print('Woof')
a, b = Cat(), Dog()
a.talk() # 'Meow'
b.talk() # 'Woof'
Meow
Woof
通过 isinstance()
函数能够判断一个对象是否是某个类(或其子类)的实例:
print(isinstance(a, Cat), isinstance(a, Animal))
True True
或者:type(a).__name__
'Cat'
当然,类也能够多继承。写在左侧的类的属性与办法,在继承时会被优先采纳。例如:
class Pet:
def talk(self):
print("Pet")
class Cat2(Pet, Cat):
pass
a = Cat2()
a.talk()
Pet
@property 装璜器
装璜器 @property
能够被用于限度类属性的读写行为。比方,一个一般的类,如果想封装一个属性,却容许从内部读取它的值,个别咱们用 getter
函数实现:
class Person:
def __init__(self):
self.__name = "Py"
def get_name(self):
return self.__name
a = Person()
a.get_name()
'Py'
不得不说这切实是麻烦了,代码里一堆 get 函数满天飞并不令人欢快。而且还不能遗记它是一个函数,须要在尾部加上括号。
装璜器 @property
能够将一个办法伪装成同名的属性,因而装璜了 getter
函数后,调用时就不必加上尾部的括号了:
class Person:
def __init__(self):
self.__name = "Py"
@property
def name(self):
return self.__name
a = Person()
a.name
'Py'
而且,如果你想从内部批改该属性的值,会产生谬误:
a.name = 1
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-97-8c607f2aa25b> in <module>()
----> 1 a.name = 1
AttributeError: can't set attribute
但同时,咱们也能够指定其 setter 函数(该装璜器 @age.setter 在用 @property 装璜 age 办法后会主动生成),让属性批改成为可能,甚至附加批改条件:
class Person:
def __init__(self):
self.__age = 20
@property
def age(self):
return self.__age
@age.setter
def age(self, value):
if not isinstance(value, int):
raise ValueError("Age should be an integer.")
else:
self.__age = value
a = Person()
a.age = 30
a.age
30
不传入整数会报错:
a.age = 0.5
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-100-001bfa8fe26b> in <module>()
----> 1 a.age = 0.5
<ipython-input-98-83364d5faa13> in age(self, value)
10 def age(self, value):
11 if not isinstance(value, int):
---> 12 raise ValueError("Age should be an integer.")
13 else:
14 self.__age = value
ValueError: Age should be an integer.
类的非凡属性与办法
属性 __dict__
首先是 __dict__
属性,用于查看类的属性与办法,返回一个字典。
a = MyClass()
MyClass.__dict__
mappingproxy({'__dict__': <attribute '__dict__' of 'MyClass' objects>,
'__doc__': '\n This is a class that can meow!\n',
'__module__': '__main__',
'__weakref__': <attribute '__weakref__' of 'MyClass' objects>,
'animal': 'cat',
'talk': <function __main__.MyClass.talk>})
须要留神的是,此时实例 a 的属性没有被更改过,实例的 dict 是一个空字典:
print(a.__dict__, a.animal)
{} cat
类的 __dict__
办法下的同名键,与实例具备雷同值。
MyClass.__dict__["animal"]
'cat'
一旦被从内部更改,实例 a 的 dict 字典就不再为空。
a.animal = "dog"
print(a.__dict__, a.animal)
{'animal': 'dog'} dog
属性 __slots__
从下面能够看到,非公有的类属性能够从内部更改值,而且属性还能间接从内部减少。__slots__ 属性的作用就在于使类的属性不能从内部进行更改、追加。它可能限度属性滥用,并在优化内存上也有意义。
class MySlotClass():
__slots__ = ("meow", "woof")
def __init__(self):
self.meow = "Meow"
self.woof = "Woof"
a = MySlotClass()
MySlotClass.__dict__
mappingproxy({'__doc__': None,
'__init__': <function __main__.MySlotClass.__init__>,
'__module__': '__main__',
'__slots__': ('meow', 'woof'),
'meow': <member 'meow' of 'MySlotClass' objects>,
'woof': <member 'woof' of 'MySlotClass' objects>})
此时,如果应用 a.__dict__
,后果不会返回空字典,而是会报错。
运算符重载
特地地,Python 提供了运算符重载的性能。罕用的对应如下(参考 官网页面):
办法 含意 利用
一元运算符
len 长度 len(a)
bool 逻辑值 bool(a)
neg 取负值 -a
str / repr 字符串模式 repr(a) / str(a), print(a)
二元运算符
add 加 a + b, a += b
sub 减 a – b, a -= b
mul 乘 a b, a = b
div 除 a / b, a /= b
pow 乘方 a b, a = b
radd 左加 … + a
二元关系符
lt / le 小于 / 小于等于 a < b, a <= b
gt / ge 大于 / 大于等于 a > b, a >= b
eq / ne 等于 / 不等于 a == b, a != b
比方下例中,对多个运算进行了重载,实现了二维向量在加减法上与向量、与数运算的根本定义。
class Vector:
def __init__(self, a, b):
self.a = a
self.b = b
def __add__(self, another):
if isinstance(another, Vector):
c, d = another.a, another.b
else:
c, d = another, another
return Vector(self.a + c, self.b + d)
def __radd__(self, another):
return self.__add__(another)
def __neg__(self):
return Vector(-self.a, -self.b)
def __sub__(self, another):
return self.__add__(-another)
def __str__(self):
return "Vector({},{})".format(self.a, self.b)
v1 = Vector(0,3)
v2 = Vector(5,-2)
print(v1 - 1, -v2, v1 + v2, v1 - v2)
Vector(-1,2) Vector(-5,2) Vector(5,1) Vector(-5,5)
其中,__repr__()
与 __str__()
的次要区别在于,前者在交互式步骤中显示后果,后者在 print 函数中显示后果。
例如上例,如果间接输出 v1,不会以“Vector(0,3)”的模式显示。
v1 # 在类中附加定义:__repr__ = __str__ 即可解决问题。<__main__.Vector at 0x2531c129c88>
迭代行为
在类中也能定义迭代行为,须要 __iter__() 与 __next__() 办法。
# 该例改编自官网文档
class MyClass4:
def __init__(self, lst):
self.data = lst
self.__index = len(lst)
def __iter__(self):
return self
def __next__(self):
if self.__index == 0:
raise StopIteration
self.__index -= 1
return self.data[self.__index]
a = MyClass4("Meow")
for char in a:
print(char)
w
o
e
M
罕用模块
上面介绍几个罕用的 Python 规范模块(即随 Python 装置的模块)。更多的第三方模块,例如 NumPy, pandas, matplotlib,能够参考本系列博文的其余文章。
os 模块
这个模块应该是 Python 自带模块中使用率最高的一个了。一些例子:
# import os
#
# ----- 文件操作 -----
# os.rename("old.py", "new.py") # 重命名
# os.remove("a.py") # 删除
# os.stat("b.py") # 查看文件属性
#
# ----- 门路操作 -----
# os.getcwd() # 获取当前目录
# os.chdir(r"d:\list") # 更改当前目录为
# os.chdir(os.pardir) # 返回上一级目录
# os.mkdir('newfolder') # 在当前目录新建一个文件夹
# os.listdir('c:\list') # 列出文件夹下所有文件的列表
# os.removedirs('thefolder') # 删除空文件夹
# os.path.isfile/ispath("f") # 查看门路是文件或是目录
# os.path.exists("f") # 查看门路是否存在
#
# ----- 操作平台相干 -----
# os.sep # 以后操作系统的门路分隔符
# os.linesep # 以后操作系统的换行符
# os.path.join(r"c:\abc", "d") # 连贯字串成为门路
sys 模块
个别我很少用到这个模块。可能有这么几个命令会用到:
- sys.argv:可能传递从命令行承受的参数到代码内。
- sys.platform:以后操作系统平台。
-
sys.exit():无参数时抛出 SystemExit 谬误并退出;有参数时会在退出前输入对应的字符串到屏幕。
import sys sys.platform 'win32'
一个
sys.argv
的例子:sys.argv ['e:\\python\\lib\\site-packages\\ipykernel_launcher.py', '-f', 'C:\\Users\\wklchris\\AppData\\Roaming\\jupyter\\runtime\\kernel-3724c4c9-2130-485d-b388-7a84379fd043.json']
以上不是典型的例子,因为并不是在命令行下运行的。命令行下通常有如下格局:
python test.py hello
此时,sys.argv[0] = test.py,sys.argv[1] = hello.
re 模块:正则表达式
参考本文附录。
其余模块
-
collection 模块:
- 提供了一种双端列表 deque,能够用 appendleft, extendleft, popleft 等办法从 deque 的左侧(也就是 lst[0])进行操作。留神,deque 的更新操作比 list 更快,但读取操作比 list 慢。
- 提供了一种缺省字典
defaultdict
,能够间接操作键值(即便这个键先前未定义);首次操作时会赋一个正当的初值,比方首次调用 d[“a”] += 1 而字典自身没有“a”键时,会主动初始化“a”键并赋初值 0。
- calendar 模块:判断星期、平年,输入日历等等。
- itertools 模块:在本文“迭代器”大节已进行了简要介绍。
- logging 模块:在调试中可能会应用。
- urllib 模块:这是一个 HTML 申请模块,罕用于爬虫。
调试与测试
Python 中有一些内置的方法进行调试与测试。
断言:assert
断言的含意在于,如果断言失败(False),那么代码会被终止(抛出一个AssertionError
)。比方:
n = 0
assert(n != 0)
1 / n
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
<ipython-input-112-e53f92f6c644> in <module>()
1 n = 0
----> 2 assert(n != 0)
3 1 / n
AssertionError:
与大家一贯喜爱应用的 print 调试法相比,断言语句能够用命令行参数 -O 疏忽。这样所有的 assert() 语句都不会被执行。
$ python -O main.py
日志调试:logging 模块
logging 模块反对将谬误日志输入(到控制台或者到文件)。
此乃调试神器。延长浏览:logging 官网基础教程。
import logging
# 该行也可能通过控制台:$ python main.py --log=WARNING 的形式实现
logging.basicConfig(level=logging.WARNING)
n = 0
logging.warning("n = {}".format(n))
WARNING:root:n = 0
logging 模块的灵便之处在于你能够记录信息的级别(DEBUG,INFO,WARNING,ERROR,CRITICAL
),各级别的作用如下:
- DEBUG:最具体的级别,所有具体日志都会被输入。
- INFO:检测代码是否依照预期执行。
- WARNING:非预期的事件产生了,或者可能在近期产生(例如:低磁盘空间)。但代码依然执行。
- ERROR:产生了级别更高的问题,某些性能无奈失常实现。
-
CRITICAL:严重错误,代码可能无奈持续运行。
通过filename
参数,能够将日志写入到文件。个别应用DEBUG
级别,即输入所有信息。# logging.basicConfig(filename="log.log", level=logging.DEBUG)
默认会将日志追加到文件开端,如果想要覆写文件而不是追加,应用 filemode 参数:
# logging.basicConfig(filename="log.log", filemode="w", level=logging.DEBUG)
更改日志格格局,应用 format 参数。一般来说,罕用的格局码(格局码后加 s 示意字符串)有:
%(levelname)
:以后日志字串级别。%(message)
:以后日志字串。-
%(asctime)
:以后工夫。默认 datefmt 参数为%Y-%m-%d %I:%M:%S
例子。下例会输入形如:”01/23/1900 08:05:05 PM is when this event was logged.”这样的格局。# logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p') # logging.warning('is when this event was logged.') # 罕用的格局:# logging.basicConfig(format="%(levelname)s: %(message)s")
还能够通过配置文件来代替 basicConfig 命令,并进行设置 logger 等更高级的配置。这部分能够参考:此处。
# import logging.config # logging.config.fileConfig('logging.conf')
附录:正则表达式
正则表达式的根底内容参考本博客的这篇博文:正则表达式。留神:如果要保留一个正则表达式供屡次应用,请存储其 compile
后的后果,防止重复编译。
- re.compile(exp):编译正则表达式。
-
re.compile(exp).match(str):判断正则表达式是否匹配一个字串。能够 bool() 后果来获知是否匹配。
- re.compile(exp).match(str).groups():将匹配后果返回为单个字符串(无子组时)或元组(有子组时)。
- re.compile(exp).findall(str):找出字符串中所有匹配表达式的子串。返回列表。
-
re.split(exp, str):用表达式来宰割字符串,相当于 str.split() 的增强版。
import re bool(re.match(r"\d", "1")) True phone_re = re.compile(r'\d{3,4}-\d{7,8}') phone_re.match('010-12345678').group() '010-12345678' # 如果在正则表达式中增加了子组(小括号),那么会返回子组依程序组成的一个元组 phone_re = re.compile(r'(\d{3,4})-(\d{7,8})') phone_re.match('010-12345678').groups() ('010', '12345678') phone_re = re.compile(r'\d{3,4}-\d{7,8}') # 寻找所有子串 phone_set = '010-12345678, 021-65439876' phone_re.findall(phone_set) ['010-12345678', '021-65439876'] s = 'a b c' # 用 re.split() 解决间断的空格 print(s.split(''), re.split(r"\s+", s)) ['a', 'b', '','', 'c'] ['a', 'b', 'c']