0%

Python 学习笔记

前言

最近到了暑期实习的招聘期,看着小伙伴们准备各厂的面试,刷题刷算法刷数据结构,突然感觉自己好菜啊。一说红黑树,就记得数据结构讲过,到底是什么都忘得干干净净。至于算法本身也没有好好学习过,只懂得一些基本的递归的思路。(不过这学期有选算法课,希望能好好学哈哈哈)

虽然目前的定位不是就业,但是感觉迟早要去产业界的话多刷刷题还是没有缺点的,而且刷题的过程也蛮好玩的(主要原因)。于是注册了一个 LeetCode 账号。这里发现好像国区有专属的服务,国内想去国际版的 LeetCode 貌似还很麻烦,找了很多对比,最后还是进了国区。随便试试一道题,发现居然支持十多种语言,连最近在学的 Kotlin 都有(TIOBE 排行34 的语言也太有面子了)。然鹅具体要用什么语言的时候却开始选择困难了,因为自己学语言基本都是入了门就没往深学了,都是半吊子,刷 LeetCode 的话还是希望能够让自己有一门非常熟悉的语言。Java 常年最流行语言,产业界常青藤,开发后盾、开发App、乃至web都会用到; Python 这些年也十分火热,写的方便,还有近些年大火的机器学习;作为安全专业科班出身,C、C++十分差劲也说不过去;尤其科研上准备往浏览器和 Web 方向前进, JavaScript, PHP 也要加深掌握。然后头秃了。最后思前想后还是打算做了 Python,毕竟现在 Python 和机器学习数据分析的需求量还是相当猛的(参考最近看到的各类招聘)。于是就有了这一篇博文。然后又惊喜的发现,最近刷的很舒服的 JetBrains Academy 有 python 的课程。于是打算先把 Kotlin 的这个项目刷完就去做 Python,然后尽可能坚持用 Python 刷 LeetCode。

参考资料

正文

PEP 8

PEP 即 Python Enhancement Proposal

一行不要超过 79 个字符。当然没人会记忆和数的,大部分编辑器或者 IDE 都有类似功能。

避免不必要的空格。

  • Avoid extra spaces within parentheses.

Good:

1
print('Hello!')

Bad:

1
print( 'Hello!' )
  • Avoid an extra space before an open parenthesis.

Good:

1
print('some text')

Bad:

1
print ('some text')

关于双引号,因为 Python 不区分字符和字符串,单引号双引号可以互换,所以尽可能避免用转义来打出引号。比如有单引号的字符串就用双引号括起来,有双引号的字符串就用单引号括起来。

命名变量

使用小写字母和下划线命名。使用ASCII编码(尤其不要用一些其他类似西里尔文字的字母混淆)。不要用 l 和 1,o 和 O 和 0 等容易混淆的字符。如果最合适的变量名被Python关键字占了,在后面加个下划线。

1
2
class_ = type(var)  # yes
klass = type(var) # no

注释

注释可以用 # ,这适用于大多数情况,在使用时,确保 # 前有2个空格,以及后面有1个空格。

1
2
print("Learning Python is fun!")  # This is a proper comment formatting
print("PEP-8 is important!")#This is a very bad example

多行时请分开使用 # 。 """...""" 也可以达到多行注释的效果。但是在一般情况下请不要使用这个,因为在官方推荐中,三个双引号用于 documentation strings 或者叫 docstrings ,仅用于函数、方法和其他极少数情况。

1
2
3
4
# Imagine that this is an example of a really long comment
# that we need to spread over three lines, so we continue
# to write it even here.
print("The long comment above explains this line of code.")

如果可以通过优化代码减少注释,那就 do it.

1
2
n = 0  # number of participants  # wrong
participants_num = 0

例如上面的代码,不要用第一行的写法。

另外不要写显而易见的注释。

通常,注释应该解决的是 why 而不是 what。去解释为什么编写这样的代码,而不是说明做了什么。

遍历 Dict

下面给出遍历 Dict 的 3 种方式。首先是遍历 key,直接使用 for in 会遍历 keys。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Python3 code to iterate through all keys in a dictionary 

statesAndCapitals = {
'Gujarat' : 'Gandhinagar',
'Maharashtra' : 'Mumbai',
'Rajasthan' : 'Jaipur',
'Bihar' : 'Patna'
}

print('List Of given states:\n')

# Iterating over keys
for state in statesAndCapitals:
print(state)

第二种使用 values() 方法,遍历 Value。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Python3 code to iterate through all values in a dictionary 

statesAndCapitals = {
'Gujarat' : 'Gandhinagar',
'Maharashtra' : 'Mumbai',
'Rajasthan' : 'Jaipur',
'Bihar' : 'Patna'
}

print('List Of given capitals:\n')

# Iterating over values
for capital in statesAndCapitals.values():
print(capital)

最后一种使用 items() 方法同时遍历 key 和 value。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Python3 code to iterate through all key, value  
# pairs in a dictionary

statesAndCapitals = {
'Gujarat' : 'Gandhinagar',
'Maharashtra' : 'Mumbai',
'Rajasthan' : 'Jaipur',
'Bihar' : 'Patna'
}

print('List Of given states and their capitals:\n')

# Iterating over values
for state, capital in statesAndCapitals.items():
print(state, ":", capital)

List comprehension

终于遇到了一个自己不熟的知识点!刷 hyperSkill 刷的前期感觉都是基础知识,哈哈哈终于刷到了一个感觉扩展知识面的知识点。

List comprehension 能够利用任意 iterable 的对象生成列表,语法如下。

1
2
3
4
5
6
7
# list comprehension syntax
new_list = [x for x in some_iterable]

# 上面代码所等效的代码
new_list = []
for x in some_iterable:
new_list.append(x)

该语法不只是复制列表用的,可以在过程中做一些额外操作。

1
2
3
# squared numbers
numbers = [1, 2, 3]
square_list = [x * x for x in numbers] # [1, 4, 9]

此外还可以用 if 筛选元素。

1
2
3
4
5
6
7
8
# list comprehension with condition
new_list = [x for x in some_iterable if condition]

# like
# conditions with functions
text = ["function", "is", "a", "synonym", "of", "occupation"]
words_tion = [word for word in text if word.endswith("tion")]
# result: ["function", "occupation"]

接下来是超级硬核的嵌套 List Comprehension。不过不建议使用这种方式,因为可读性会下降,而且读起来很反直觉。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# original list
school = [["Mary", "Jack", "Tiffany"],
["Brad", "Claire"],
["Molly", "Andy", "Carla"]]

student_list = []
for class_group in school:
for student in class_group:
student_list.append(student)

print(student_list)
# result: ["Mary", "Jack", "Tiffany", "Brad", "Claire", "Molly", "Andy", "Carla"]

# 和前面用嵌套 for 循环一样
student_list = [student for class_group in school for student in class_group]

print(student_list)
# result: ["Mary", "Jack", "Tiffany", "Brad", "Claire", "Molly", "Andy", "Carla"]

Scope

LEGB 法则:

Python 中的变量遵循 LEGB rule. 这意味着解释器按照下面的顺序寻找变量。:

  1. Locals. 函数体内未声明 global 的变量.
  2. Enclosing. 从里到外嵌套的函数的 Locals.
  3. Globals. 在模块最顶层定义的变量或者 声明了 global 的变量.
  4. Built-in. Python 内建变量。

在对 global 变量进行修改前必须在函数内使用 global 声明。

nonlocal 可以使得我们可以对外界的变量赋值(不是 global的)

globalnonlocal 都不是很常用,因为这样会使得程序难以阅读。

Any and All

两个函数都只能对 iterable 的对象使用。如果传入的对象不是 bool 类,结果参考 bool() 函数,比如 1 会被当做 True, 0 会被当做 False。

Any 当输入中的元素中存在 True 时 返回 True。传入空对象时,返回 False。

All 当输入中的元素中全为True 时 返回 True。传入空对象时,返回 True。

1
2
3
rocket_science_scores = [0, -0, 0.0, +0]
any(rocket_science_scores) # False
all(rocket_science_scores) # False

字符串操作

startswith()

startswith() 函数可以判断一个字符串是否在另一个字符串的开头。

参考资料 : https://www.runoob.com/python/att-string-startswith.html

简单用法:

1
2
str = "this is string example....wow!!!";
print(str.startswith( 'this' ));

格式化字符串

参考资料: https://docs.python.org/3.6/library/string.html#format-specification-mini-language

最基本的 string % value 是模仿了C语言的语法。

首先是 str. format() 方法。

1
2
3
4
5
6
7
8
9
10
11
12
print('Mix {}, {} and a {} to make an ideal omelet.'.format('2 eggs', '30 g of milk', 'pinch of salt'))
# Mix 2 eggs, 30 g of milk and a pinch of salt to make an ideal omelet.
print('{0} in the {1} by Frank Sinatra'.format('Strangers', 'Night'))
# trangers in the Night by Frank Sinatra
print('{1} in the {0} by Frank Sinatra'.format('Strangers', 'Night'))
# Night in the Strangers by Frank Sinatra
print('The {film} at {theatre} was {adjective}!'.format(film='Lord of the Rings',
adjective='incredible',
theatre='BFI IMAX'))
# The Lord of the Rings at BFI IMAX was incredible!
print('The {0} was {adjective}!'.format('Lord of the Rings', adjective='incredible'))
# The Lord of the Rings was incredible!

然后是 Formatted string literals ,又叫 f-strings 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
name = 'Elizabeth II'
title = 'Queen of the United Kingdom and the other Commonwealth realms'
reign = 'the longest-lived and longest-reigning British monarch'
f'{name}, the {title}, is {reign}.'
Elizabeth II, the Queen of the United Kingdom and the other Commonwealth realms, is the longest-lived and longest-reigning British monarch.

hundred_percent_number = 1823
needed_percent = 16
needed_percent_number = hundred_percent_number * needed_percent / 100

print(f'{needed_percent}% from {hundred_percent_number} is {needed_percent_number}')
# 16% from 1823 is 291.68

print(f'Rounding {needed_percent_number} to 1 decimal place is {needed_percent_number:.1f}')
# Rounding 291.68 to 1 decimal place is 291.7

命令行参数

你可以在命令行中使用类似下面的命令运行python脚本。后面由空格分隔的部分是参数。

1
python C:\python_scripts\add_two_numbers.py 11 44

使用 sys 模块来获取命令行参数。

1
2
3
4
5
6
7
8
9
import sys  # first, we import the module

args = sys.argv # we get the list of arguments
first_num = float(args[1]) # convert arguments to float
second_num = float(args[2])

product = first_num * second_num

print("The product of " + args[1] + " times " + args[2] + " equals " + str(product))

sys.argv[0] 是脚本的名字,具体是相对路径还是绝对路径取决于你是怎么输入命令行的命令的,和你命令行输入的路径一致。另外记住传入的参数类型均为 字符串。

Context Manager : With as

Context Manager 这个名字一听属实有点唬人,了解一下就发现是之前经常见到的 with ... as 语句。

这种语法的引入背景是当用户开启过多的文件时,会报错,而很多时候用户忘记或者不知道何时关闭文件,使用 Context Manager 可以高效的管理,并且避免文件开启过多带来的各种问题。

基本语法如下:

1
2
3
# invoking a context manager
with statement as variable_name:
...

statement 需要支持 context manager 的操作,这里列举一些常用的 context manager:

  • File objects (and other streams like io.StringIO or io.BytesIO);
  • Sockets;
  • Locks and semaphores in the threading module;
  • Database connections;
  • Mock objects.

不过绝大多数时候,我们都是在于文件交互时使用这个语法。

使用这种语法就可以不用在调用 f.close() 了,但是你也可以调用但是并不建议这样做。

使用样例:

1
2
3
4
with open('tarantino.txt', 'r', encoding='utf-8') as f:
for line in f:
# we use strip() to get rid of newline symbols
print(line.strip())

另外还可以同时打开多个文件:

1
2
3
4
with open('tarantino.txt', 'r', encoding='utf-8') as in_file, \
open('tarantino_lowercase.txt', 'w', encoding='utf-8') as out_file:
for line in in_file:
out_file.write(line.lower())

Set

set 中存储独立元素,正如数学中的 set (集合)。

set 中添加元素 使用 add 方法,删除元素使用 remove 方法或者 discard 方法,唯一区别在于 remove 方法在没有对应元素时会报错 Key Error。

1
2
3
4
5
6
7
8
nums = {1, 2, 2, 3}
nums.add(5)
print(nums) # {1, 2, 3, 5}
nums.remove(2)
print(nums) # {1, 3, 5}
empty_set = set()
empty_set.remove(2)
print(empty_set) # KeyError: 2

另外一个有意思的是 pop 方法,由于 set 中元素是无序的,pop 方法会删除并返回一个随机元素。

update 方法可以把传入的参数内的每一个元素都 add 进 set。

Python 字符串中的 f

最初看到了一个奇怪的语法:print(f’My name is {name}’)

好奇里面的 f 是什么,毕竟 b 什么的很常见, f 第一次见。搜了一下就是把大括号里的字符串当变量输出。太方便了!之前一直憨憨的用字符串拼接和str()。

1
2
3
4
5
6
7
8
import time
t0 = time.time()
time.sleep(1)
name = 'processing'
# 以 f开头表示在字符串内支持大括号内的python 表达式
print(f'{name} done in {time.time() - t0:.2f} s')
# **输出:**
# processing done in 1.00 s

杂项

多行字符串使用 三引号。

1
2
3
4
5
print("""This
is
a
multi-line
string""")

Python 的 for … else 语法:在 python 中,for … else 表示这样的意思,for 中的语句和普通的没有区别,else 中的语句会在循环正常执行完(即 for 不是通过 break 跳出而中断的)的情况下执行,while … else 也是一样。

1
2
3
4
5
6
7
8
for num in range(10,20):  # 迭代 10 到 20 之间的数字
for i in range(2,num): # 根据因子迭代
if num%i == 0: # 确定第一个因子
j=num/i # 计算第二个因子
print '%d 等于 %d * %d' % (num,i,j)
break # 跳出当前循环
else: # 循环的 else 部分
print num, '是一个质数'

可以用 maxsplit 参数来指定split 函数能split 几次,最后返回的 list 的元素个数会是 maxsplit + 1 .

1
2
3
4
5
6
7
8
# maxsplit example
definition = input() # 'Coin of the realm is the legal money of the country'

definition.split("of", 1)
# ['Coin ', ' the realm is the legal money of the country']

definition.split("of")
# ['Coin ', ' the realm is the legal money ', ' the country']

使用 Named Argument 时,需要注意,Named Argument 需要放到最后面。

1
2
greet("Frodo", surname="Baggins")  # Hello, Frodo Baggins
greet(name="Frodo", "Baggins") # SyntaxError: positional argument follows keyword argument

parameters 是形参,arguments 是实参。

Python 字符串排序时,是先按第一位按字母序排序,然后第二位、第三位等等。举个栗子。

1
2
3
>>> a = ['a','b','c','ab','ac','ba','bc','ca','cb','abc','acb']
>>> sorted(a)
['a', 'ab', 'abc', 'ac', 'acb', 'b', 'ba', 'bc', 'c', 'ca', 'cb']