[代码重构](master): 添加了Python的一些学习文件

Python文件更新
master
土豆兄弟 2 years ago
parent b876e68764
commit 9eb9f5f1bc

@ -524,9 +524,406 @@ entries = [
通常会保证其至少留有 1/3 的剩余空间。随着元素的不停插入,当剩余空间小于 1/3 时Python 会重新获取更大的内存空间, 通常会保证其至少留有 1/3 的剩余空间。随着元素的不停插入,当剩余空间小于 1/3 时Python 会重新获取更大的内存空间,
扩充哈希表。不过,这种情况下,表内所有的元素位置都会被重新排放。 扩充哈希表。不过,这种情况下,表内所有的元素位置都会被重新排放。
### 3. 字典和集合 ### 3. 深入浅出字符串
- 字符串基础
- 字符串是由独立字符组成的一个序列,通常包含在单引号('')双引号("")或者三引号之中(''' '''或""" """,两者一样),比如下面几种写法。
```python
name = 'jason'
city = 'beijing'
text = "welcome to jike shijian"
```
- Python 中单引号、双引号和三引号的字符串是一模一样的,没有区别
```python
s1 = 'hello'
s2 = "hello"
s3 = """hello"""
s1 == s2 == s3
True
```
- Python 同时支持这三种表达方式,很重要的一个原因就是,这样方便你在字符串中,内嵌带引号的字符串。
```python
"I'm a student"
```
- Python 的三引号字符串,则主要应用于多行字符串的情境,比如函数的注释等等。
```python
def calculate_similarity(item1, item2):
"""
Calculate similarity between two items
Args:
item1: 1st item
item2: 2nd item
Returns:
similarity score between item1 and item2
"""
```
- PS 这个注释的模板很好
- Python 也支持转义字符。所谓的转义字符,就是用反斜杠开头的字符串,来表示一些特定意义的字符。
![深入字符串1](pic/深入字符串1.png)
- 在转义字符的应用中,最常见的就是换行符'\n'的使用。比如文件读取,如果我们一行行地读取,那么每一行字符串的末尾,都会包含换行符'\n'。而最后做数据处理时,我们往往会丢掉每一行的换行符。
- 字符串的常用操作
- 可以把字符串想象成一个由单个字符组成的数组所以Python 的字符串同样支持索引,切片和遍历等等操作。
```python
name = 'jason'
name[0]
'j'
name[1:3]
'as'
```
- 和其他数据结构,如列表、元组一样,字符串的索引同样从 0 开始index=0 表示第一个元素(字符),[index:index+2] 则表示第 index 个元素到 index+1 个元素组成的子字符串。
- 遍历字符串同样很简单,相当于遍历字符串中的每个字符。
```python
for char in name:
print(char)
j
a
s
o
n
```
- 特别要注意Python 的字符串是不可变的immutable。因此用下面的操作来改变一个字符串内部的字符是错误的不允许的。
```python
s = 'hello'
s[0] = 'H'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
```
- Python 中字符串的改变,通常只能通过创建新的字符串来完成。比如上述例子中,想把'hello'的第一个字符'h',改为大写的'H',我们可以采用下面的做法:
```python
# 第一种方法,是直接用大写的'H',通过加号'+'操作符,与原字符串切片操作的子字符串拼接而成新的字符串。
s = 'H' + s[1:]
# 第二种方法,是直接扫描原字符串,把小写的'h'替换成大写的'H',得到新的字符串。
s = s.replace('h', 'H')
```
- 着重讲解一下,使用加法操作符'+='的字符串拼接方法。因为它是一个例外,打破了字符串不可变的特性。
```python
str1 += str2 # 表示 str1 = str1 + str2
```
- 举例
```python
s = ''
for n in range(0, 100000):
s += str(n)
```
- 每次循环,似乎都得创建一个新的字符串;而每次创建一个新的字符串,都需要 O(n) 的时间复杂度。因此,总的时间复杂度就为 O(1) + O(2) + … + O(n) = O(n^2)。这样到底对不对呢?
- 这个结论只适用于老版本的 Python 了。自从 Python2.5 开始每次处理字符串的拼接操作时str1 += str2Python 首先会检测 str1 还有没有其他的引用。如果没有的话,
就会尝试原地扩充字符串 buffer 的大小,而不是重新分配一块内存来创建新的字符串并拷贝。这样的话,上述例子中的时间复杂度就仅为 O(n) 了。
- 以后你在写程序遇到字符串拼接时,如果使用’+='更方便,就放心地去用吧,不用过分担心效率问题了。
- 对于字符串拼接问题,除了使用加法操作符,我们还可以使用字符串内置的 join 函数。string.join(iterable),表示把每个元素都按照指定的格式连接起来。
```python
l = []
for n in range(0, 100000):
l.append(str(n))
l = ' '.join(l)
```
- 由于列表的 append 操作是 O(1) 复杂度,字符串同理。因此,这个含有 for 循环例子的时间复杂度为 n*O(1)=O(n)。
- 字符串的分割函数 split()。string.split(separator),表示把字符串按照 separator 分割成子字符串,并返回一个分割后子字符串组合的列表。
- 它常常应用于对数据的解析处理,比如我们读取了某个文件的路径,想要调用数据库的 API去读取对应的数据我们通常会写成下面这样
```python
def query_data(namespace, table):
"""
given namespace and table, query database to get corresponding
data
"""
path = 'hive://ads/training_table'
namespace = path.split('//')[1].split('/')[0] # 返回'ads'
table = path.split('//')[1].split('/')[1] # 返回 'training_table'
data = query_data(namespace, table)
```
- 此外,常见的函数还有:
- string.strip(str),表示去掉首尾的 str 字符串;
- string.lstrip(str),表示只去掉开头的 str 字符串;
- string.rstrip(str),表示只去掉尾部的 str 字符串。
- 这些在数据的解析处理中同样很常见。比如很多时候,从文件读进来的字符串中,开头和结尾都含有空字符,我们需要去掉它们,就可以用 strip() 函数:
```python
s = ' my name is jason '
s.strip()
'my name is jason'
```
- 当然Python 中字符串还有很多常用操作比如string.find(sub, start, end),表示从 start 到 end 查找字符串中子字符串 sub 的位置等等
- 字符串的格式化
- 通常我们使用一个字符串作为模板模板中会有格式符。这些格式符为后续真实值预留位置以呈现出真实值应该呈现的格式。字符串的格式化通常会用在程序的输出、logging 等场景。
- 比如我们有一个任务,给定一个用户的 userid要去数据库中查询该用户的一些信息并返回。而如果数据库中没有此人的信息我们通常会记录下来这样有利于往后的日志分析或者是线上 bug 的调试等等。
- 我们通常会用下面的方法来表示:
```python
print('no data available for person with id: {}, name: {}'.format(id, name))
```
- 其中的 string.format(),就是所谓的格式化函数;而大括号{}就是所谓的格式符,用来为后面的真实值——变量 name 预留位置。如果id = '123'、name='jason',那么输出便是:
```python
'no data available for person with id: 123, name: jason'
```
- 不过要注意string.format() 是最新的字符串格式函数与规范。自然,我们还有其他的表示方法,比如在 Python 之前版本中,字符串格式化通常用 % 来表示,那么上述的例子,就可以写成下面这样:
```python
print('no data available for person with id: %s, name: %s' % (id, name))
```
- 其中 %s 表示字符串型,%d 表示整型等等,这些属于常识,你应该都了解。
- 推荐使用 format 函数,毕竟这是最新规范,也是官方文档推荐的规范。
- PS: Python要想玩好, 一定要学会 更加 pythonic更加高效的办法来编写代码
- 举例: 两个字符串拼接操作
- 1
```python
s = ''
for n in range(0, 100000):
s += str(n)
```
- 2
```python
l = []
for n in range(0, 100000):
l.append(str(n))
s = ' '.join(l)
```
- 3 pythonic更加高效的办法
```python
s = " ".join(map(str, range(0, 10000)))
```
### 4. Python “黑箱”:输入与输出
- 输入输出基础
- 最简单直接的输入来自键盘操作,比如下面这个例子。
```python
name = input('your name:')
gender = input('you are a boy?(y/n)')
###### 输入 ######
your name:Jack
you are a boy?
welcome_str = 'Welcome to the matrix {prefix} {name}.'
welcome_dic = {
'prefix': 'Mr.' if gender == 'y' else 'Mrs',
'name': name
}
print('authorizing...')
print(welcome_str.format(**welcome_dic))
########## 输出 ##########
authorizing...
Welcome to the matrix Mr. Jack.
```
- input() 函数暂停程序运行同时等待键盘输入直到回车被按下函数的参数即为提示语输入的类型永远是字符串型str
- 注意初学者在这里很容易犯错下面的例子我会讲到。print() 函数则接受字符串、数字、字典、列表甚至一些自定义类的输出。
- 再来看下面这个例子。
```python
a = input()
1
b = input()
2
print('a + b = {}'.format(a + b))
########## 输出 ##############
a + b = 12
print('type of a is {}, type of b is {}'.format(type(a), type(b)))
########## 输出 ##############
type of a is <class 'str'>, type of b is <class 'str'>
print('a + b = {}'.format(int(a) + int(b)))
########## 输出 ##############
a + b = 3
```
- 这里注意,把 str 强制转换为 int 请用 int(),转为浮点数请用 float()。而在生产环境中使用强制转换时,请记得加上 try except
- Python 对 int 类型没有最大限制(相比之下, C++ 的 int 最大为 2147483647超过这个数字会产生溢出但是对 float 类型依然有精度限制。这些特点,除了在一些算法竞赛中要注意,在生产环境中也要时刻提防,
避免因为对边界条件判断不清而造成 bug 甚至 0day危重安全漏洞
- 虽然输入输出和类型处理事情简单,但我们一定要慎之又慎。毕竟相当比例的安全漏洞,都来自随意的 I/O 处理。
- 文件输入输出
- 命令行的输入输出,只是 Python 交互的最基本方式,适用一些简单小程序的交互。而生产级别的 Python 代码,大部分 I/O 则来自于文件、网络、其他进程的消息等等。
- 接下来,我们来详细分析一个文本文件读写。假设我们有一个文本文件 in.txt内容如下
```txt
I have a dream that my four little children will one day live in a nation where they will not be judged by the color of their skin but by the content of their character. I have a dream today.
I have a dream that one day down in Alabama, with its vicious racists, . . . one day right there in Alabama little black boys and black girls will be able to join hands with little white boys and white girls as sisters and brothers. I have a dream today.
I have a dream that one day every valley shall be exalted, every hill and mountain shall be made low, the rough places will be made plain, and the crooked places will be made straight, and the glory of the Lord shall be revealed, and all flesh shall see it together.
This is our hope. . . With this faith we will be able to hew out of the mountain of despair a stone of hope. With this faith we will be able to transform the jangling discords of our nation into a beautiful symphony of brotherhood. With this faith we will be able to work together, to pray together, to struggle together, to go to jail together, to stand up for freedom together, knowing that we will be free one day. . . .
And when this happens, and when we allow freedom ring, when we let it ring from every village and every hamlet, from every state and every city, we will be able to speed up that day when all of God's children, black men and white men, Jews and Gentiles, Protestants and Catholics, will be able to join hands and sing in the words of the old Negro spiritual: "Free at last! Free at last! Thank God Almighty, we are free at last!"
```
- 让我们来做一个简单的 NLP自然语言处理任务。
- 基本步骤,也就是下面的四步:
- 读取文件;
- 去除所有标点符号和换行符,并把所有大写变成小写;
- 合并相同的词,统计每个词出现的频率,并按照词频从大到小排序;
- 将结果按行输出到文件 out.txt
- 用 Python 如何解决这个问题
```python
import re
# 你不用太关心这个函数
def parse(text):
# 使用正则表达式去除标点符号和换行符
text = re.sub(r'[^\w ]', ' ', text)
# 转为小写
text = text.lower()
# 生成所有单词的列表
word_list = text.split(' ')
# 去除空白单词
word_list = filter(None, word_list)
# 生成单词和词频的字典
word_cnt = {}
for word in word_list:
if word not in word_cnt:
word_cnt[word] = 0
word_cnt[word] += 1
# 按照词频排序
sorted_word_cnt = sorted(word_cnt.items(), key=lambda kv: kv[1], reverse=True)
return sorted_word_cnt
with open('in.txt', 'r') as fin:
text = fin.read()
word_and_freq = parse(text)
with open('out.txt', 'w') as fout:
for word, freq in word_and_freq:
fout.write('{} {}\n'.format(word, freq))
########## 输出 (省略较长的中间结果) ##########
and 15
be 13
will 11
to 11
the 10
of 10
a 8
we 8
day 6
...
old 1
negro 1
spiritual 1
thank 1
god 1
almighty 1
are 1
```
- 不用太关心 parse() 函数的具体实现,你只需要知道,它做的事情是把输入的 text 字符串,转化为我们需要的排序后的词频统计。而 sorted_word_cnt 则是一个二元组的列表list of tuples
- 计算机中文件访问的基础知识。事实上计算机内核kernel对文件的处理相对比较复杂涉及到内核模式、虚拟文件系统、锁和指针等一系列概念
- 先要用 open() 函数拿到文件的指针。其中,第一个参数指定文件位置(相对位置或者绝对位置);第二个参数,如果是 'r'表示读取,
如果是'w' 则表示写入,当然也可以用 'rw' 表示读写都要。a 则是一个不太常用但也很有用的参数表示追加append这样打开的文件如果需要写入会从原始文件的最末尾开始写入。
- 在拿到指针后,我们可以通过 read() 函数,来读取文件的全部内容。代码 text = fin.read() ,即表示把文件所有内容读取到内存中,并赋值给变量 text。这么做自然也是有利有弊
- 优点是方便,接下来我们可以很方便地调用 parse 函数进行分析;
- 缺点是如果文件过大,一次性读取可能造成内存崩溃。
- 这时,我们可以给 read 指定参数 size ,用来表示读取的最大长度。还可以通过 readline() 函数每次读取一行这种做法常用于数据挖掘Data Mining中的数据清洗在写一些小的程序时非常轻便。
如果每行之间没有关联,这种做法也可以降低内存的压力。而 write() 函数,可以把参数中的字符串输出到文件中,也很容易理解。
- 这里我需要简单提一下 with 语句后文会详细讲到。open() 函数对应于 close() 函数,也就是说,如果你打开了文件,在完成读取任务后,就应该立刻关掉它。而如果你使用了 with 语句,
就不需要显式调用 close()。在 with 的语境下任务执行完毕后close() 函数会被自动调用,代码也简洁很多。
- 所有 I/O 都应该进行错误处理。因为 I/O 操作可能会有各种各样的情况出现而一个健壮robust的程序需要能应对各种情况的发生而不应该崩溃
- JSON 序列化与实战
- 设想一个情景,你要向交易所购买一定数额的股票。那么,你需要提交股票代码、方向(买入 / 卖出)、订单类型(市价 / 限价)、价格(如果是限价单)、数量等一系列参数,而这些数据里,有字符串,有整数,有浮点数,甚至还有布尔型变量,全部混在一起并不方便交易所解包。
- 要讲的 JSON ,正能解决这个场景。你可以把它简单地理解为两种黑箱:
- 第一种,输入这些杂七杂八的信息,比如 Python 字典,输出一个字符串;
- 第二种,输入这个字符串,可以输出包含原始信息的 Python 字典。
- 具体代码如下:
```python
import json
params = {
'symbol': '123456',
'type': 'limit',
'price': 123.4,
'amount': 23
}
params_str = json.dumps(params)
print('after json serialization')
print('type of params_str = {}, params_str = {}'.format(type(params_str), params))
original_params = json.loads(params_str)
print('after json deserialization')
print('type of original_params = {}, original_params = {}'.format(type(original_params), original_params))
########## 输出 ##########
# after json serialization
# type of params_str = <class 'str'>, params_str = {'symbol': '123456', 'type': 'limit', 'price': 123.4, 'amount': 23}
# after json deserialization
# type of original_params = <class 'dict'>, original_params = {'symbol': '123456', 'type': 'limit', 'price': 123.4, 'amount': 23}
```
- json.dumps() 这个函数,接受 Python 的基本数据类型,然后将其序列化为 string
- json.loads() 这个函数,接受一个合法字符串,然后将其反序列化为 Python 的基本数据类型。
- 请记得加上错误处理。不然,哪怕只是给 json.loads() 发送了一个非法字符串,而你没有 catch 到,程序就会崩溃了。
- 如果我要输出字符串到文件,或者从文件中读取 JSON 字符串,又该怎么办呢?
- 你仍然可以使用上面提到的 open() 和 read()/write() ,先将字符串读取 / 输出到内存,再进行 JSON 编码 / 解码,当然这有点麻烦。
```python
import json
params = {
'symbol': '123456',
'type': 'limit',
'price': 123.4,
'amount': 23
}
with open('params.json', 'w') as fout:
params_str = json.dump(params, fout)
with open('params.json', 'r') as fin:
original_params = json.load(fin)
print('after json deserialization')
print('type of original_params = {}, original_params = {}'.format(type(original_params), original_params))
########## 输出 ##########
# after json deserialization
# type of original_params = <class 'dict'>, original_params = {'symbol': '123456', 'type': 'limit', 'price': 123.4, 'amount': 23}
```
- 简单清晰地实现了读写 JSON 字符串的过程。当开发一个第三方应用程序时,你可以通过 JSON 将用户的个人配置输出到文件,方便下次程序启动时自动读取。这也是现在普遍运用的成熟做法。
- JSON 是唯一的选择吗?显然不是,它只是轻量级应用中最方便的选择之一。据我所知,在 Google有类似的工具叫做 Protocol Buffer
- 相比于 JSON它的优点是生成优化后的二进制文件因此性能更好。但与此同时生成的二进制序列是不能直接阅读的。它在 TensorFlow 等很多对性能有要求的系统中都有广泛的应用。
### 5. 修炼基本功:条件与循环
### 6. 异常处理:如何提高程序的稳定性?

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Loading…
Cancel
Save