python在19年就已经在工作中使用了,主要不是主力语言,学的可能不够细致全面,目前所在的公司有用它做bi的数据报表数据拉取处理及几个游戏的后端接口,那就再次学习下这门胶水语言,看极客时间《python核心技术与实战专栏》重学一下
其他python知识点,见notebook
元祖和列表
列表和元组都是有序的,可以存储任意数据类型的集合
- 列表是动态的,长度可变,可以随意的增加、删减或改变元素。列表的存储空间略大于元组,性能略逊于元组。
- 元组是静态的,长度大小固定,不可以对元素进行增加、删减或者改变操作。元组相对于列表更加轻量级,性能稍优。
1l = [1, 2, 3, 4]
2l[3] = 40 # 和很多语⾔类似,python中索引同样从0开始,l[3]表⽰访问列表的第四个元素
3
4
5tup = (1, 2, 3, 4)
6new_tup = tup + (5, ) # 创建新的元组new_tup,并依次填充原元组的值
7
8
9# 列表和元组都可以随意嵌套
10l = [[1, 2, 3], [4, 5]] # 列表的每⼀个元素也是⼀个列表
11tup = ((1, 2, 3), (4, 5, 6)) # 元组的每⼀个元素也是⼀元组
12
13
14# 互转
15list((1, 2, 3))
16[1, 2, 3]
17tuple([1, 2, 3])
18(1, 2, 3)
常用函数
- count(item)表示统计列表/元组中item出现的次数。
- index(item)表示返回列表/元组中item第一次出现的索引。
- list.reverse()和list.sort()分别表示原地倒转列表和排序(注意,元组没有内置的这两个函数)。
- reversed()和sorted()同样表示对列表/元组进行倒转和排序,但是会返回一个倒转后或者排好序的新的列表/元组。
代码性能
1python3 -m timeit 'empty_list=[]'
2python3 -m timeit 'empty_list=list()'
字典、集合
集合并不支持索引操作,因为集合本质上是一个哈希表,和列表不一样.
创建、访问,增删改
1d1 = {'name': 'jason', 'age': 20, 'gender': 'male'}
2d2 = dict({'name': 'jason', 'age': 20, 'gender': 'male'})
3d3 = dict([('name', 'jason'), ('age', 20), ('gender', 'male')])
4d4 = dict(name='jason', age=20, gender='male')
5d1 == d2 == d3 ==d4
6True
7
8s1 = {1, 2, 3}
9s2 = set([1, 2, 3])
10s1 == s2
11True
12
13d = {'name': 'jason', 'age': 20}
14d.get('name')
15'jason'
16d.get('location', 'null')
17'null'
18
19d = {'name': 'jason', 'age': 20}
20d['gender'] = 'male' # 增加元素对'gender': 'male'
21d['dob'] = '1999-02-01' # 增加元素对'dob': '1999-02-01'
22d
23{'name': 'jason', 'age': 20, 'gender': 'male', 'dob': '1999-02-01'}
24d['dob'] = '1998-01-01' # 更新键'dob'对应的值
25d.pop('dob') # 删除键为'dob'的元素对
26'1998-01-01'
27d
28{'name': 'jason', 'age': 20, 'gender': 'male'}
29
30s = {1, 2, 3}
31s.add(4) # 增加元素 4 到集合
32s
33{1, 2, 3, 4}
34s.remove(4) # 从集合中删除元素 4
35s
36{1, 2, 3}
判断一个元素在不在字典或集合内,我们可以用value in dict/set 来判断
1s = {1, 2, 3}
21 in s True
310 in s False
4
5
6d = {'name': 'jason', 'age': 20}
7'name' in d True
8'location' in d False
字典键或值,进行升序或降序排序
1# 字段排序
2d = {'b': 1, 'a': 2, 'c': 10}
3d_sorted_by_key = sorted(d.items(), key=lambda x: x[0]) # 根据字典键的升序排序
4d_sorted_by_value = sorted(d.items(), key=lambda x: x[1]) # 根据字典值的升序排序
5d_sorted_by_key
6[('a', 2), ('b', 1), ('c', 10)]
7d_sorted_by_value
8[('b', 1), ('a', 2), ('c', 10)]
集合排序
1s = {3, 4, 2, 1}
2sorted(s) # 对集合的元素进行升序排序
3[1, 2, 3, 4]
遍历元祖列表
1def find_product_price(products, product_id):
2 for id, price in products:
3 if id == product_id:
4 return price
5 return None
6
7products = [
8 (143121312, 100),
9 (432314553, 30),
10 (32421912367, 150)
11]
哈希表除了字典本身的结构,会把索引和哈希值、键、值单独分开
1Indices
2----------------------------------------------------
3None | index | None | None | index | None | index ...
4----------------------------------------------------
5
6Entries
7--------------------
8hash0 key0 value0
9---------------------
10hash1 key1 value1
11---------------------
12hash2 key2 value2
13---------------------
14 ...
15---------------------
深入浅出字符串
Python 中单引号、双引号和三引号的字符串是一模一样的
多行注释
1"""
2注释内容
3xxxx
4"""
字符串切片
1name = 'jason'
2name[0]
3'j'
4name[1:3]
5'as'
Python 的字符串是不可变的(immutable)
1s = ''
2for n in range(0, 100000):
3 s += str(n)
str1 += str2 ,Python 首先会检测 str1 还有没有其他的引用。如果没有的话,就会尝试原地扩充字符串 buffer 的大小,而不是重新分配一块内存来创建新的字符串并拷贝。这样的话,上述例子中的时间复杂度就仅为 O(n) 了 (python2.5+ ,所以用+ 不必担心效率问题)
字符串格式化
1print('no data available for person with id: {}, name: {}'.format(id, name)) # 新的版本
2print('no data available for person with id: %s, name: %s' % (id, name))
输入输出
1open() 函数拿到文件的指针。其中,第一个参数指定文件位置(相对位置或者绝对位置);第二个参数,如果是 'r'表示读取,如果是'w' 则表示写入,当然也可以用 'rw' ,表示读写都要。a 则是一个不太常用(但也很有用)的参数,表示追加(append),这样打开的文件,如果需要写入,会从原始文件的最末尾开始写入
1with open('in.txt', 'r') as fin:
2 text = fin.read() #读取全部
3 # readline() 读一行 ,read 也可以读指定size
4word_and_freq = parse(text)
5with open('out.txt', 'w') as fout:
6 for word, freq in word_and_freq:
7 fout.write('{} {}\n'.format(word, freq))
JSON 序列化与实战
- json.dumps() 这个函数,接受 Python 的基本数据类型,然后将其序列化为 string;
- 而 json.loads() 这个函数,接受一个合法字符串,然后将其反序列化为 Python 的基本数据类型。
1import json
2
3params = {
4 'symbol': '123456',
5 'type': 'limit',
6 'price': 123.4,
7 'amount': 23
8}
9
10params_str = json.dumps(params)
11
12print('after json serialization')
13print('type of params_str = {}, params_str = {}'.format(type(params_str), params))
14
15original_params = json.loads(params_str)
如果是文件 ,可以用以下方式
1import json
2
3params = {
4 'symbol': '123456',
5 'type': 'limit',
6 'price': 123.4,
7 'amount': 23
8}
9
10with open('params.json', 'w') as fout:
11 params_str = json.dump(params, fout)
12
13with open('params.json', 'r') as fin:
14 original_params = json.load(fin)
15
16print('after json deserialization')
17print('type of original_params = {}, original_params = {}'.format(type(original_params), original_params))
条件、循环
1if condition_1:
2 statement_1
3elif condition_2:
4 statement_2
5else:
6 statement_n
1for item in <iterable>:
2 ...
3# 字典遍历
4d = {'name': 'jason', 'dob': '2000-01-01', 'gender': 'male'}
5for k in d: # 遍历字典的键
6 print(k)
7for v in d.values(): # 遍历字典的值
8 print(v)
9for k, v in d.items(): # 遍历字典的键值对
10 print('key: {}, value: {}'.format(k, v))
集合遍历
1for index, item in enumerate(l):
2 ...
3
4for index in range(0, len(l)):
5 ...
6
7
8# 多利用 continue,break 来简化代码,方便观看,减少层级
9
10
11
12# 性能 range > while
13#range是c 写的, 因为 i 是整型,是 immutable,i += 1 相当于 i = new int(i + 1)
14for i in range(0, 1000000):
15 pass
16
17i = 0
18while i < 1000000:
19 i += 1
表达式,语法糖
1# expression1 if condition else expression2 for item in iterable
2y = [value * 2 + 5 if value > 0 else -value * 2 + 5 for value in x]
3
4# 没有else
5# expression for item in iterable if condition
6
7text = ' Today, is, Sunday'
8text_list = [s.strip() for s in text.split(',') if len(s.strip()) > 3]
9print(text_list)
10['Today', 'Sunday']
11
12
13
14[(xx, yy) for xx in x for yy in y if xx != yy] #等价于下面
15
16l = []
17for xx in x:
18 for yy in y:
19 if xx != yy:
20 l.append((xx, yy))
1attributes = ['name', 'dob', 'gender']
2values = [['jason', '2000-01-01', 'male'],
3['mike', '1999-01-01', 'male'],
4['nancy', '2001-02-01', 'female']
5]
6
7# expected outout:
8[{'name': 'jason', 'dob': '2000-01-01', 'gender': 'male'},
9{'name': 'mike', 'dob': '1999-01-01', 'gender': 'male'},
10{'name': 'nancy', 'dob': '2001-02-01', 'gender': 'female'}]
11
12
13[{ attributes[i]: value[i] for i in range(len(attributes)) } for value in values]
14[dict(zip(attributes, value)) for value in values]
异常处理
能用代码搞定是否异常的, 就别乱用异常
1try:
2 s = input('please enter two numbers separated by comma: ')
3 num1 = int(s.split(',')[0].strip())
4 num2 = int(s.split(',')[1].strip())
5 ...
6except (ValueError, IndexError) as err:
7 print('Error: {}'.format(err))
8
9print('continue')
10
11try:
12 s = input('please enter two numbers separated by comma: ')
13 num1 = int(s.split(',')[0].strip())
14 num2 = int(s.split(',')[1].strip())
15 ...
16except ValueError as err:
17 print('Value Error: {}'.format(err))
18except IndexError as err:
19 print('Index Error: {}'.format(err))
20except:
21 print('Other error')
22finally:
23 f.close()
24print('continue')
自定义异常
1class MyInputError(Exception):
2 """Exception raised when there're errors in input"""
3 def __init__(self, value): # 自定义异常类型的初始化
4 self.value = value
5 def __str__(self): # 自定义异常类型的 string 表达形式
6 return ("{} is invalid input".format(repr(self.value)))
7
8try:
9 raise MyInputError(1) # 抛出 MyInputError 这个异常
10except MyInputError as err:
1try:
2 data = json.loads(raw_data)
3 ....
4except JSONDecodeError as err:
5 print('JSONDecodeError: {}'.format(err))
自定义函数
1def name(param1, param2, ..., paramN):
2 statements
3 return/yield value # optional
(比如 C 语言)不一样的是,def 是可执行语句,这意味着函数直到被调用前,都是不存在的。当程序调用函数时,def 语句才会创建一个新的函数对象,并赋予其名字。
主程序调用函数时,必须保证这个函数此前已经定义过,不然就会报错,函数内部调用就没关系
1my_func('hello world')
2def my_func(message):
3 print('Got a message: {}'.format(message))
4# 输出
5# NameError: name 'my_func' is not defined
函数的嵌套
第一,函数的嵌套能够保证内部函数的隐私。内部函数只能被外部函数所调用和访问,不会暴露在全局作用域,因此,如果你的函数内部有一些隐私数据(比如数据库的用户、密码等),不想暴露在外,那你就可以使用函数的的嵌套,将其封装在内部函数中,只通过外部函数来访问。比如:
1def connect_DB():
2 def get_DB_configuration():
3 ...
4 return host, username, password
5 conn = connector.connect(get_DB_configuration())
6 return conn
第二,合理的使用函数嵌套,能够提高程序的运行效率
类型检测只检测一次
1def factorial(input):
2 # validation check
3 if not isinstance(input, int):
4 raise Exception('input must be an integer.')
5 if input < 0:
6 raise Exception('input must be greater or equal to 0' )
7 ...
8
9 def inner_factorial(input):
10 if input <= 1:
11 return 1
12 return input * inner_factorial(input-1)
13 return inner_factorial(input)
14
15
16print(factorial(5))
不能在函数内部随意改变全局变量的值
1MIN_VALUE = 1
2MAX_VALUE = 10
3def validation_check(value):
4 ...
5 MIN_VALUE += 1
6 ...
7validation_check(5)
8# UnboundLocalError: local variable 'MIN_VALUE' referenced before assignment
Python 的解释器会默认函数内部的变量为局部变量,但是又发现局部变量 MIN_VALUE 并没有声明,因此就无法执行相关操作。所以,如果我们一定要在函数内部改变全局变量的值,就必须加上 global 这个声明
1MIN_VALUE = 1
2MAX_VALUE = 10
3def validation_check(value):
4 global MIN_VALUE
5 ...
6 MIN_VALUE += 1
7 ...
8validation_check(5)
局部变量名和全局重复,那么就使用局部变量
1MIN_VALUE = 1
2MAX_VALUE = 10
3def validation_check(value):
4 MIN_VALUE = 3
5 ...
对于嵌套函数来说,内部函数可以访问外部函数定义的变量,但是无法修改,若要修改,必须加上 nonlocal 这个关键字
1def outer():
2 x = "local"
3 def inner():
4 nonlocal x # nonlocal 关键字表示这里的 x 就是外部函数 outer 定义的变量 x
5 x = 'nonlocal'
6 print("inner:", x)
7 inner()
8 print("outer:", x)
9outer()
10# 输出
11#inner: nonlocal
12#outer: nonlocal
13
14#. 如果没加nonlocal 那么输出
15
16#inner: nonlocal
17#outer: local
闭包
闭包其实和刚刚讲的嵌套函数类似,不同的是,这里外部函数返回的是一个函数,而不是一个具体的值。返回的函数通常赋于一个变量,这个变量可以在后面被继续执行调用
1def nth_power(exponent):
2 def exponent_of(base):
3 return base ** exponent
4 return exponent_of # 返回值是 exponent_of 函数
5
6square = nth_power(2) # 计算一个数的平方
7cube = nth_power(3) # 计算一个数的立方
8square
9# 输出
10<function __main__.nth_power.<locals>.exponent(base)>
11
12cube
13# 输出
14<function __main__.nth_power.<locals>.exponent(base)>
15
16print(square(2)) # 计算 2 的平方
17print(cube(2)) # 计算 2 的立方
18# 输出
194 # 2^2
208 # 2^3
1def nth_power_rewrite(base, exponent):
2 return base ** exponent
3# 不使用闭包
4res1 = nth_power_rewrite(base1, 2)
5res2 = nth_power_rewrite(base2, 2)
6res3 = nth_power_rewrite(base3, 2)
7...
8
9# 使用闭包 ,更加简洁
10square = nth_power(2)
11res1 = square(base1)
12res2 = square(base2)
13res3 = square(base3)
14...
闭包常常和装饰器(decorator)一起使用
匿名函数
1lambda argument1, argument2,... argumentN : expression
2square = lambda x: x**2
3square(3)
4
59
第一,lambda 是一个表达式(expression),并不是一个语句(statement)。
因此,lambda 可以用在一些常规函数 def 不能用的地方
1[(lambda x: x*x)(x) for x in range(10)]
2# 输出
3[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
lambda 可以被用作某些函数的参数,而常规函数 def 也不能
1l = [(1, 20), (3, 0), (9, 10), (2, -1)]
2l.sort(key=lambda x: x[1]) # 按列表中元祖的第二个元素排序
3print(l)
4# 输出
5[(2, -1), (3, 0), (9, 10), (1, 20)]
第二,lambda 的主体是只有一行的简单表达式,并不能扩展成一个多行的代码块
Python 之所以发明 lambda,就是为了让它和常规函数各司其职:lambda 专注于简单的任务,而常规函数则负责更复杂的多行逻辑。关于这点,Python 之父 Guido van Rossum 曾发了一篇文章解释
函数作用(lambda也有这些作用)
- 减少代码的重复性;
- 模块化代码。
函数 map(function, iterable) 的第一个参数是函数对象,第二个参数是一个可以遍历的集合,它表示对 iterable 的每一个元素,都运用 function 这个函数
1python3 -mtimeit -s'xs=range(1000000)' 'map(lambda x: x*2, xs)'
22000000 loops, best of 5: 171 nsec per loop
3
4python3 -mtimeit -s'xs=range(1000000)' '[x * 2 for x in xs]'
55 loops, best of 5: 62.9 msec per loop
6
7python3 -mtimeit -s'xs=range(1000000)' 'l = []' 'for i in xs: l.append(i * 2)'
85 loops, best of 5: 92.7 msec per loop
map是最快的,因为是c写的
filter(function, iterable)
1l = [1, 2, 3, 4, 5]
2new_list = filter(lambda x: x % 2 == 0, l) # [2, 4]
reduce(function, iterable)
1l = [1, 2, 3, 4, 5]
2product = reduce(lambda x, y: x * y, l) # 1*2*3*4*5 = 120
对一个字典,根据值进行由高到底的排序
1sorted(d.items(),key=lambda x:x[1],reverse=True)
面向对象
1class Document():
2 WELCOME_STR = 'Welcome! The context for this book is {}.'
3
4 def __init__(self, title, author, context):
5 print('init function called')
6 self.title = title
7 self.author = author
8 self.__context = context # __ 开头的属性是私有属性
9
10
11 def intercept_context(self, length):
12 self.__context = self.__context[:length]
13
14 # 类函数
15 @classmethod
16 def create_empty_book(cls, title, author):
17 return cls(title=title, author=author, context='nothing')
18
19 # 成员函数
20 def get_context_length(self):
21 return len(self.__context)
22
23 # 静态函数
24 @staticmethod
25 def get_welcome(context):
26 return Document.WELCOME_STR.format(context)
继承
1class Entity():
2 def __init__(self, object_type):
3 print('parent class init called')
4 self.object_type = object_type
5
6 # 子类要用 get_context_length ,必须重写,不然报错
7 def get_context_length(self):
8 raise Exception('get_context_length not implemented')
9
10 def print_title(self):
11 print(self.title)
12
13class Document(Entity):
14 def __init__(self, title, author, context):
15 print('Document class init called')
16 Entity.__init__(self, 'document')
17 self.title = title
18 self.author = author
19 self.__context = context
20
21 def get_context_length(self):
22 return len(self.__context)
抽象类
抽象类是一种特殊的类,它生下来就是作为父类存在的,一旦对象化就会报错。同样,抽象函数定义在抽象类之中,子类必须重写该函数才能使用。相应的抽象函数,则是使用装饰器 @abstractmethod 来表示。
1from abc import ABCMeta, abstractmethod
2
3class Entity(metaclass=ABCMeta):
4 @abstractmethod
5 def get_title(self):
6 pass
7
8 @abstractmethod
9 def set_title(self, title):
10 pass
11
12class Document(Entity):
13 def get_title(self):
14 return self.title
15
16 def set_title(self, title):
17 self.title = title
18
19document = Document()
20document.set_title('Harry Potter')
21print(document.get_title())
22
23entity = Entity()
24
25########## 输出 ##########
26
27Harry Potter
28
29---------------------------------------------------------------------------
30TypeError Traceback (most recent call last)
31<ipython-input-7-266b2aa47bad> in <module>()
32 21 print(document.get_title())
33 22
34---> 23 entity = Entity()
35 24 entity.set_title('Test')
36
37TypeError: Can't instantiate abstract class Entity with abstract methods get_title, set_title