书读百遍,其义自现。每次重学基础,总有值得记录的地方。

大连-渔人码头

和其他语言不一样的除法

python 中,2/2 的结果是小数,而不是我们熟知的整数。

1
2
3
4
# <class 'float'>
type(2/2)
# <class 'int'>
type(2//2)

进制表示法

1
2
3
4
5
6
7
8
0b10 -> 二进制
0o11 -> 八进制
0x15 -> 十六进制

bin() -> 二进制转换函数
int() -> 十进制转换函数
oct() -> 八进制转换函数
hex() -> 十六进制转换函数

数据类型

boolNumber 大分类下面的一种,而不是单独一个类。

1
2
int(True) = 1
bool(0) = False

可变类型和不可变类型

  • int str tuple 是不可改变类型
  • list set dict 是可改变类型
1
2
3
4
5
6
7
# 证明
# id() 函数可以得到变量的内存地址,下面代码两次 a 变量的内存地址是不一样的

a = 'hello'
id(a)
a = a + ' world'
id(a)

为什么会存在 tuple:编程注重稳定性,tuple 是不可改变类型,也就是说 a[1] = '2'a.append(3) 是报错的,但是很多场景下我们需要 tuple 就够了,不需要 list 提供的各种数组方法。

身份运算符

身份运算符 is 和 ==

1
2
3
4
5
6
# Case 1
# 下面比较的是两个集合,是无需的,== 比较的是值,因此表达式两边相等
a = {1,2,3}
b = {2,1,3}
a is b # False
a == b # True
1
2
3
4
5
# Case 2
c = (1,2,3)
d = (2,1,3)
a == d # False
a is b # False
1
2
3
4
# Case 3
e = None
id(e) # 4412745832
e is None # True

对象的三个特征和三个方法

  • 数据对象的三个特征:id,value,type
  • 分别对象三个方法:id(), ==, isinstance()

不用 type 而用 isinstance 是因为 type 不能判断子类

函数多值返回

python 函数是可以有多个返回值的,多个返回值会封装到元组中

1
2
3
4
5
def add(a, b):
return a, b

c, d = add(1, 2)
print([c, d])

序列解包

正常的序列解包
a, b, c = [1, 2, 3]

可变参数的解包赋值

1
2
3
4
5
6
def add(param, *args, **kwargs):
print(param, args, kwargs)

args = (1, 2, 3)
kwargs = {'a': 1, 'b': 2}
add(1, *args, **kwargs)

所有类型和 bool 类型的对应

js 中一样,python 中所有的类型都可以转换成 bool 类型。

1
2
3
4
5
bool(0) == False
bool([]) == False
bool({}) == False
bool('') == False
bool(None) == False

除了上面的,其余的结构基本都为 True

枚举类型

枚举的好处:

  • 枚举定义的值不可变
  • 防止相同的值的功能
  • 枚举可遍历
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from enum import Enum

class VIP(Enum):
YELLOW = 1
# 如果有相同的数值会被当作上一个的别名,是不会被遍历出来
YELLOW_ALISA = 1
GREEN = 4
BLACK = 3


print(VIP.GREEN) # 枚举的类型
print(VIP.GREEN.name) # 枚举的名字
print(VIP.GREEN.value) # 枚举的值

# 转成枚举类型
a = 1
print(VIP(1))

面向对象

要区分类成员和对象成员,类成员是直接定义在类上的变量,对象成员是通过 self 定义的。如果一个变量在对象中找不到,则会去类上去找。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Student(object):
name = 'ss'
age = 9
sex = 'f'

def __init__(self, name, age):
self.name = name
# 这句话没有意义,前后 age 都是形参的 age
age = age
# 这样可以在方法中访问类变量
print(self.__class__.age)


ss = Student('Wang Qiang', 18)
print(ss.name) # 对象成员
print(ss.age) # 来自类成员,因为对象成员中没有
print(ss.sex) # 来自类成员,因为对象成员中没有
print(Student.name) # 类成员

print(ss.__dict__) # 打印出所有的对象成员
print(Student.__dict__) # 打印出所有的类成员

类方法是用来操作类成员变量的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Student(object):
name = 'ss'
age = 9
sex = 'f'

def __init__(self, name, age):
self.name = name
self.age = age

@classmethod
def opr_info(cls):
cls.age = cls.age + 1


ss = Student(name='Wang Qiang', age=18)
# 类方法是专门用于操作类变量的
Student.opr_info()
# 对象也可以调用类方法,但是不推荐这么做
ss.opr_info()

python 对私有变量的保护很弱

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 下面代码中,其实 python 将私有变量 __name 改名为 _Student__name,因此其对私有变量对保护非常弱
class Student(object):
__name = 'ss'
__age = 9

def __init__(self, name, age):
self.__name = name
self.__age = age


ss1 = Student(name='Wang Qiang', age=18)
ss1.__name = 'Haha'
print(ss1.__name) # 强制给 ss1 设置变量 __name,而不是覆盖类的私有变量

ss2 = Student(name='Cao Lilu', age=18)
print(ss2.__name) # 报错

了解面向对象下 python 的灵活

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Student(object):
__name = 'ss'
__age = 9

def __init__(self, name, age):
self.__name = name
self.__age = age

def do_homework(self):
print('I am instresting code')


ss1 = Student(name='Wang Qiang', age=18)
# 以下两个代码也是可以执行的,了解 python 的灵活,下面代码是不可取的
Student.do_homework(ss1)
Student.do_homework('xxx')

使用列表模仿 switch case

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
day = 1

def get_zero():
return 'Sunday'

def get_one():
return 'Monday'

switcher = {
0: get_zero,
1: get_one,
}

day_name = switcher.get(day, None)()
print(day_name)

列表推导式 & 字典列表推导式

列表推导式

1
2
item_list = [x for x in [1, 2, 3, 4] if x > 2]
print(item_list)

字典的列表推导式

1
2
3
4
5
6
7
8
9
students = {
'喜小乐': 18,
'周小云': 20,
'横小五': 15,
}

b = (key for key, value in students.items())
for x in b:
print(x)

逻辑运算符

我们知道逻辑运算符 andor 的规则分别是“全真才真”和“全假才假”。下面代码中,数字也分别对应相应的布尔值,因此如果读到第一个就能判断出结果,就返回第一个,否则返回第二个。

1
2
3
4
5
6
7
8
print(1 and 0)  # 0
print(0 and 1) # 0
print(1 and 2) # 2
print(2 and 1) # 1

print(0 or 1) # 1
print(1 or 0) # 1
print(1 or 2) # 1

python 包的导入

__all__ = ['a', 'b'] 表示在用 from a import * 的时候,只能导入 ab__all__ 是约束导出。

当一个 python 文件被当作主文件入口的时候,其名字会被强制更改为 __main__,一个 python 程序只有一个入口,因此其他模块的名字都是正常的模块名。

关于 python 程序包的绝对引入和相对引入

  • 首先要知道在 java 中一个文件就是一个类,一个文件中的类名和文件名是一样的,但是在 python 中一个文件则是一个模块,类是在模块里面的。
  • python 中的顶级包是和入口文件在同一级别目录的(项目的跟路径文件名不是顶级包名),因此模块导入的时候程序找包一般是从顶级模块开始找的。
  • 在顶级包之内的包是可以使用相对导入的,一个点代表当前路径,两个点代表上一级,依次类推。
  • 但是和顶级包同等级的文件是不能使用相对导入的,因为 python 会把 __package__ 的名字改为 __main__,这时候就不是正常的包寻址名字了,因此是没发正常导入包的。

dataclass 来简化类的实例化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from dataclasses import dataclass

@dataclass
class Student(object):
name: str
age: int
school_name: str

# def __init__(self, name, age, school_name):
# self.name = name
# self.age = age
# self.school_name = school_name

def print_data(self):
print([str(self.name), str(self.age), str(self.school_name)])


student = Student('Xiaohua', 18, 'Tsinghua')
student.print_data()

实现迭代器

python 中,决定是否是可叠迭代对象的关键点是是否实现了 __iter____next__ 这两个魔法函数,下面例子就是将一个类的实例变成了可迭代对象。
迭代器具有一次性,迭代完一次就能再次遍历了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class BookCollection(object):
def __init__(self):
self.books = ['Book01', 'Book02', 'Book03']
self.cur = 0

def __iter__(self):
return self

def __next__(self):
if self.cur >= len(self.books):
raise StopIteration()

r = self.books[self.cur]
self.cur += 1
return r


books = BookCollection()
for book in books:
print(book)

生成器

用生成器生成 0 到 10000 数据的例子。

1
2
3
4
5
6
7
8
9
10
11
def gen(max):
n = 0
while n < max:
n += 1
yield n


g = gen(10000)
print(g.__next__())
print(g.__next__())
print(g.__next__())

其实可以用列表推导式,但是这会很占用内存,因为列表推导式生成的 10000 个数据都要存到数组中,也就是存到内存中。但是生成器只是函数计算,并不会吧 10000 个数据都存放到内存中。

其实元组的列表推导式生成的就是生成器。

1
2
3
4
n = (i for i in range(1, 10001))
print(n.__next__())
print(n.__next__())
print(n.__next__())

闭包

下面是闭包的例子,注意 __closure__ 存在的条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def curve_pre():
a = 25

def curve(x):
# 如果这里重新对 a 赋值,那么 __closure__ 就不存在了,
# 因为没有了对环境变量对引用,也就不存在闭包了
# a = 20
return a * x * x

return curve


a = 10
f = curve_pre()
print(f.__closure__)
print(f.__closure__[0].cell_contents)
print(f(2))

下面用闭包解决梯度赋值问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def factory(pos):
def go(step):
# 这里为解决下面变量未声明赋值的问题,去父作用域寻找引用
nonlocal pos
new_pos = pos + step
pos = new_pos
return new_pos

return go


tourist = factory(0)
print(tourist(2))
print(tourist(3))
print(tourist(5))

wraps 装饰器解决自定义装饰器的缺点

wraps 这个装饰器可以将 func 函数的信息复制到 wrapper 函数上,解决被装饰函数信息改变问题,因为被装饰对象其实已经不是原函数了,比如函数的 __name__ 的变化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import time
from functools import wraps

def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(time.time())
func(*args, **kwargs)

return wrapper


@decorator
def foo(param):
print('This is the root func ' + param)


foo('Teapot')

python 函数式编程

reduce 函数和 lambda 的使用

1
2
3
4
5
6
from functools import reduce

list_a = [1, 2, 3, 4, 5, 6]
list_b = [1, 2, 3, 4, 5, 6]
r = map(lambda x, y: x * x + y, list_a, list_b)
print(list(r))

经典累加问题

1
2
3
list_x = [1, 2, 3, 4, 5, 6, 7, 8]
r = reduce(lambda x, y: x + y, list_x)
print(r)

海象运算符(python 3.8之后)

下面代码会通过 :=b 进行赋值,而不需要我们单独拉出 bif 语句之外进行赋值

1
2
3
4
5
# walrus
a = 'python'

if (b := len(a)) > 5:
print('The true length of b is:' + str(b))

杂记 & 技巧

输出原始 \n

1
2
3
4
# 会吧 \n 解释成真正的换行
print('hello \nworld')
# 前面加上 r 会以原始字符串输出
print(r'hello \nworld')

定义一个空集合

1
s = set()

元组和类型

  • type(('1')) 结果为 int,原因在于 python 会把小括号解析为普通的括号而不是 tuple
  • 如果要声明一个元素的元组,就要 ('1',)

python 之禅

import this 可以打印出 python 之禅

技巧:ab 不同同时为 False

a or b

对象的 bool 不一定为 True

对象的布尔值不一定为 True,这取决于 __len____bool__ 魔法函数的返回值,如果有 __bool__ 这个正宗方法则取决于 __bool__,优先级高于 __len__

1
2
3
4
5
6
7
8
9
10
11
12
13
class Test(object):
def __init__(self):
pass

# def __bool__(self):
# return False

def __len__(self):
return 0


test = Test()
print(bool(test))

vscode 配置 python linter

1
2
3
4
5
{
"python.linting.pyLintEnabled": false,
"python.linting.flake8Enabled": true,
"python.linting.enabled": true,
}

References:

[1] 人生苦短,快用 python