基本输入输出、数据类型和转换
# 基本输入输出
x = input("please input a string")
print(x)
print('hello, world!')
y = x.split() # 把字符串以空格为分隔符,返回分割后形成的多个字符串的列表
# 基本数据类型
x = 1 # int
x = 3.14 # float
x = 2+9j # complex
x = 'hello' # python'', ""用法一样,都用于字符串
x = True # bool
y = False
x = (1, 'hello', False, 2+9j) # 元组
x = [1, 'hello', False, 2+9j] # 列表
x = {'hello': 20, 'jack': 10} # 字典
x = {'tom', 18, 71} # 集合
a, b, c = 1, 2, 3 # python支持这种多变量赋值
# 类型转换
int(x)
float(x)
eval(x) # 字符串转化为python执行语句
str(x)
基本运算语句、条件语句、格式化输出
基本运算
计算: + , − , ∗ , / , / / , % , ∗ ∗ +,-,*,/,//,\%,** +,−,∗,/,//,%,∗∗ ( / / /总是得到float类型; / / // //用于int间的运算,得商; ∗ ∗ ** ∗∗是乘方; python同样支持增强型运算符 + = , − = , % = +=, -=, \%= +=,−=,%=等等)
关系: > , < , < = , > = , = = , ! = , i n ( 属 于 ) >, < ,<=, >=, ==, !=, in(属于) >,<,<=,>=,==,!=,in(属于)
逻辑: a n d , o r , n o t and,or,not and,or,not (相当于C/C++的&&, ||, !)
eg:
2 ** 3 == 8
5 // 3 == 1
5 % 3 == 2
True == 1 in [1, 2, 3]
False == True and False
True == True or False
False == not True
条件语句
python的缩进和C/C++的{}一样,有语法含义,表示逻辑的嵌套。
x = eval(input())
if x > 0:
print('greater than zero!')
else if x == 0:
print('zero')
else:
print('less than zero!')
格式化输出
x = 23
print("%d, %.2f, %s" % (x, 23.45, 'hello'))
print()结尾默认加**\n**,可用end更改:
print('hello, world!', end='') # 结尾不换行
print('hello, world!', end='(') # 结尾带(
print('hello, world!', end=' by python')
print()支持以,分割的多个元素输出,以一个空格分割
print('34', x, 1, 2, 3, end='')
循环、异常处理
for
for i in range(5): # [0,5), step = 1
print(i, end=',') # >>0,1,2,3,4
for i in range(3, 12, 3): # [3,12), step=3 (negative number is allowed)
print(i, end=',') # >>3,6,9
for i in [2, 'hello', 2.3, False]: # traverse a list
print(i)
while
x = int(input())
while x > 2:
x -= 1
if x == 5:
break
elif x == 6:
continue
else: # conduct when while-condition is false, ignored if while-loop is broken
print("x <= 2")
eg(三个数的最小公倍数):
s = input().split()
m = n = max(s[0], s[1], s[2])
while True:
if( m % s[0] == 0 and m % s[1] ==0 and m %s[2] == 0):
print(m)
break
m += n
异常和异常处理
常见异常(run time error):
- 不合理的类型转换
- 数学上无意义的运算
- 不合理的两种数据类型间运算
- 下标越界
- 输入已结束,仍执行input()[OJ做题常见]
异常处理形如:
try:
a = int(input())
print(100 // n)
except: # try中发生异常,跳入except;不发生异常则无视except中的语句
print('error')
print('end')
还可进一步获知错误类型:
try:
a = int(input())
print(100 // n)
except Exception as e:
print(e)
print('end')
eg:
try:
while True:
s = input().split()
print(s)
except:
pass # do nothing
函数
基本语句
函数定义形如:
def myFunc(x, y): # 形参
return x + y, x - y
a, b = myFunc(3, 4)
def myfunc2():
x = [1, 2, 3]
return x
y = myFunc2()
print(y, end='')
python的函数可返回多个数,可返回组合数据类型
无返回值,则只写return, 后不接东西
def myFunc(x):
x[0] = 1
return
变量、全局变量
x = 1
def f1():
x = 2 # 内部声明赋值:局部变量
return x
def f2():
return x + 1 # 内部不声明:全局变量
def f3():
global x #global声明本函数中所用的x是全局变量
return x - 1
python的内置函数
int(x)
float(x)
str(x)
eval(x)
ord('P') #字符转十进制ASCLL码
chr(44) #十进制ASCLL码转字符
x = [1, 2, 3, 0]
len(x)
max(x)
min(x)
max(1, 2, 3)
min(1, 2, 3)
abs(-3)
isinstance(x, list) # 返回bool, 查询x是否是某类型(基本数据类型int, float, complex |组合数据类型str, tuple, list, set)
字符串
python变量的指针本质
python所有变量都是指针。 = = =使指针指向一个地方; i s is is判断是否指向一个地方; = = == ==单纯判断内容是否一样;
a = [1, 2]
b = [1, 2]
print(a is b) # >>False
print(a == b) # >>True
a = b
print(a is b) # >>True
a[0] = 0
print(b[0]) # >>0 (因为指向同一个地方)
通过指针,可以函数中改变形参指向的地方的内容,和C/C++一样。
def swap(x, y):
tmp = x[0]
x[0] = y[0]
y[0] = tmp
转义字符
print('\\ \t \n \* \. and so on') # \
# \* \. and so on
字符串前面加r,规定不转义
print(r'\\ \t \n \* \. and so on') # \\ \t \n \* \. and so on
切片
方式与matlab一样。切片同样适用于元组和列表。
a = 'abcde'
a[1:3] # bc 左闭右开
a[0:5:2] # ace 步长为2(步长支持负数)
a[-3:-1] # cd 起终点支持负数(想象成一个环)
a[2:] # cde 省略终点,则取到最后
a[:4] # abc 省略起点,从头开始取
a[::-1] # edcba 反转字符串
字符串的分割
单分隔符分割
用s.split(x)函数,以字符串x为分隔符,实现字符串分割,返回列表。
a = 'aa.&.bb..&.cc.&.dd'
print(a.split('.')) # >>['aa', '&', 'bb', '', '&', 'cc', '&', 'dd'] 连续两个分隔符的情况,出现一个空字符串
print(a.split('&')) # >>['aa.', '.bb..', '.cc.', '.dd']
print(a.split('..')) # >>['aa.&.bb', '&.cc.&.dd']
多分隔符分割——使用正则表达式
import re # import语句导入模块
s = '-What would you like?\n-Three apples and a banana. How much?'
x = '-|\?|\n| |\.' # 用|隔开不同分隔符,注意转义字符
print(re.split(x, s)) # 用正则表达式x分割字符串s
常用字符串函数
s = 'AABBAAb'
m = s.count('AA') # 返回字符串'AA'出现次数
m = len(s) # 字符串长度
s1 = s.upper(s) # 串中全部小写转大写(字符串s本身不变,字符串是不可变数据类型)
s2 = s.lower(s) # 串中全部大写转小写(字符串s本身不变,字符串是不可变数据类型)
m = s.find('AA') # 从头找'AA'字串,返回下标,找不到返回-1
m = s.rfind('AA') # 从尾找'AA'字串,返回下标,找不到返回-1
m = s.index('AA') # 从头找'AA'字串,返回下标,找不到抛出异常
m = s.rindex('AA') # 从尾找'AA'字串,返回下标,找不到抛出异常
m = s.find('b', 3) # find()还能指定查找起点
s3 = s.replace('AA', 'cc') # 替换
False == s.islower() # 判断出现的字母是否全是小写
False == s.isupper() # 判断出现的字母是否全是大写
False == s.isdigit() # 判断字符串是否仅含有数字
True == s.startswith('AAB') #是否以某字符串开头
True == s.endswith('Ab') #是否以某字符串结尾
s4 = s.strip() # s除去两端空格字符后的字符串
s5 = s.lstrip() # s除去左端空格字符后的字符串
s6 = s.rstrip() # s除去右端空格字符后的字符串
s7 = s.strip('Ab') #s除去两端出现在'Ab'中字符('A','b')后的字符串
s7 = s.strip('Ab') #s除去左端出现在'Ab'中字符('A','b')后的字符串
s7 = s.strip('Ab') #s除去右端出现在'Ab'中字符('A','b')后的字符串
字符串的格式化
{序号:<^>宽度.精度 类型}
<、^、>对应左对齐、中对齐、右对齐
x = 'hello, {0}{1:>10}, you get ${2:0.4f}' # 宽度可以是0
y = '{0:>7.5f}' #右对齐,至少占7字符宽度,精度5位的浮点数
x1 = x.format('Mr', 'Jack', 12.34)
z = 'Today is {0}, {1}.{2}.'
z1 = z.format('Monday', 'February', '14th')
元组
元组是不可变数据类型,元素可以为任意数据类型。不能增减,修改数据,或改变顺序。
但是若元组的元素中有可变组合类型,则可以修改该可变组合类型的元素。
tuple1 = ('hello', True, 3.14, 233, [1, 2, 3])
tuple2 = 'hello', True, 3.14, 233, [1, 2, 3] # 可不加小括号
tuple3 = 'hello', # 尾部加逗号,认为是含一个元素的元组
tuple1[4][0] = 100 # 有元素是列表,列表含有的元素可以修改
元组的元素仍然用下标访问;元组切片与字符串相同;可以连接组合创建新的元组;元组比大小是元素逐个比大小,直到分出胜负。
print(tuple[1])
print(tuple1[:3])
tuple4 = tuple2 + tuple3
tuple5 = (1, 2, 3) * 3
True == tuple4 > tuple1
列表
列表可变, 可增删、修改元素,改变顺序等。元素可以为任意数据类型。
基本操作
建立、增删、修改、切片、比较
empty = []
list0 = ['BUAA', 'Beihang University', 2.71828, 11]
list1 = [True, False]
list0[-1] = 1111
list0 += list1 # 列表相加
list0.extend(list1) # 与上等效
del list0[-1] #按下标删除
list0.pop(-1) #与上等效
list0.append([0, 1]) #增加一个元素, 与列表相加不同,此处列表整体作为一个元素添加
list2 = list0[1::2] #切片与字符串、元组相同
['hello', 'apple'] > ['apple', 'hello'] # 列表比大小与元组相同
a += c, a = a + c对列表不同
a1 = a = b =[1, 2]
a += [3] # a, b指向相同, 改a也改了b
a1 = a1 + [3] # 对a1再赋值, 不影响b
列表乘法
注意中括号嵌套的规律。
print([True] * 3) # >>[True, True, True]
a = [1]
b = a * 2 # >>[1, 1]
b = [a] * 2 # >>[[1], [1]]
排序——使用排序函数
缺省规则排序
a = [23, 1, -10, 34, 4, 3.99]
b = sorted(a) # a不变,返回正序的a
a.sort() # a改变,元素按正序排列
a.sort(reverse = True) # a改变,元素按倒序排列
stu = [('Lisa', 'A', 19), ('Mike', 'B', 12), ('Ann', 'C', 11), ('Emma', 'B', 14)]
stu.sort() # 默认按名字-等级-年龄排序
元组不能改变,因此只能使用
自定义规则排序
def myKey(x):
x % 10 / 10
a.sort(key = myKey)
s = 'A message from my hometown'
li0 = sorted(s.split(), key=str.lower) # 不区分大小写排序
stu1 = sorted(stu, key=lambda x: x[2]) #按x[2]排序
lambda表达式
lambda x: x[2] # 一个函数,参数是x,返回x[2]
k = lambda x, y: x + y # k是一个函数,返回x+y
print(k(2.3, 3.7)) # >>6.0
多级排序
def k(x):
return x[2], x[1], x[0]
stu2 = sorted(stu, key=k)
列表相关函数
基本操作
a.count(1) # 数1出现次数
a.append([3, 5]) # 尾部加元素
a.extend([3, 5]) # 等价于a += [3, 5]
a.insert(0, 'a') # 下标处插入
a.remove('a') # 若没有该元素,引发异常
a.reverse() # 翻转
a.index(4) # 查找,找不到则引发异常
a.index(4, 1) # 可指定开始查找的下标
映射函数map(func, seq)
将一个序列seq(原像)映射到另一个序列(像),func是映射的规则。它返回一个延时求值对象,可转化为list, tuple, set…
s = [1, 3, 5]
a = map(lambda x: x + 1, s) # a是延时求值对象
print(tuple(a))
print(list(a))
x, y, z = map(int, input().split())
print(x, y, z)
原序列可以是多个:
q0 = [0, 1, 2]
q1 = [1, 2, 3]
q2 = [2, 3, 4]
a = list(map(lambda x, y, z: x + y + z, q0, q1, q2))
a == [3, 6, 9]
过滤函数filter(func, seq)
从序列中筛选出令func返回值是True的元素。它返回一个延时求值对象,可转化为list, tuple, set…
def func(x):
return x % 2 == 0
a = [0, 1, 2, 3, 4, 5, 6, 7]
print(list(filter(func, a))) # >>[0, 2, 4, 6]
列表、元组生成式
[1 for i in range(3)] # >>[1, 1, 1]
[x**2 for x in range(3)] # >>[0, 1, 4]
[m + n for m in 'abc' for n in 'cba'] # >>['ac', 'bb', 'ca']
w = ['a', 'b', True, 3.14, 2]
[s.upper() for i in w if isinstance(i, str)] # >>['A', 'B']
print(tuple(x + 1 for i in range(5))) # 元组的生成也类似,需要用tuple()函数
二维列表
一种错误的生成二维列表的方法:
a = [0, 0, 0]
b = [a] * 3 # >> [[0, 0, 0], [0, 0, 0], [0, 0, 0]] 此时,三行都指向同一个地方
b[1][1] = 1
print(b) # >> [[0, 1, 0], [0, 1, 0], [0, 1, 0]]
生成二维列表 法1:
lst = []
for i in range(3):
lst.append([0, 0, 0, 0]) # 3 * 4 matrix
生成二维列表 法2:
[[0 for i in range(4)] for j in range(3)] # 3 * 4 matrix
生成二维列表 法3:
m = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
生成二维元组使用tuple()函数,方法类似。
列表拷贝
a = [1, [1, 2], 3]
b = a[:] # 这种写法使b, a指向的对象不同
b[1][0] = 100
print(a) # >>[1, [100, 2], 3] (因为b仅仅复制了[1, 2]的指针,因而a[1],b[1]依然指向同一个对象; b, a仍然“藕断丝连”
需要通过深拷贝,让a, b指向的对象完完全全分开。
import copy
b = copy.deepcopy(a)
列表、元组、字符串的相互转换
通过tuple()函数,转到元组;通过list()函数,转到列表;通过’’.join()函数,转到字符串。
tuple([1, 2, 3])
tuple('hello')
list((1, 2, 3))
list('hello')
str0 = ''.join([1, 2, 3])
str0 = ''.join((1, 2, 3))
字典
字典是键值对 key:value 的集合,通过key快速查找。
- 可变数据类型不能作为key
- 一个字典中所有元素的key各不相同
- value可赋值,因此也是指针
基本操作
empty = {} # empty dictionary
dt = {'mike': 18, 'lisa': 15, 'joe': 12, 312: 111, (1, 2): [4, 5]} # keys can't be repeated
print(dt['mike'])
print(dt[(1, 2)])
print(dt['c']) # cause exception if not exist
dt['school'] = 'buaa' # add new element if not exist
del dt[312] # deletion
print((1, 2) in dt) # >>True dt中是否有key(1, 2)
dt['Joe'] = dt.get('Joe', 0) + 1 #get(key, x)函数,字典中能找到则返回value, 不能找到则返回x
构造方法
a = dict([('Jack', 18), ('Mike', 32)])
print(a) # >>{'Jack': 18, 'Mike': 32}
b = dict(name = 'Jenny', age = 15, height = 1.75)
print(b) # >>{'name': 'Jenny', 'age': 15, 'height': 1.75}
常用函数
dt.keys() # 返回键的序列
dt.values() # 返回值的序列
dt.items() # 返回元素的序列
dt.get(key, value)
dt.pop((1, 2)) # 删除键为(1, 2)的元素,无该元素则弹出异常
c = dt.copy() # 浅拷贝
import copy
c = copy.deepcopy(dt) # 深拷贝
dt.clear() # 清空字典
上述的“序列”不是list, tuple或set类型。
eg:
dt = {'mike': 18, 'lisa': 15, 'joe': 12}
print(dt.values()) # >>dict_values([18, 15, 12])
print(dt.keys()) # >>dict_keys(['mike', 'lisa', 'joe'])
print(dt.items()) # >>dict_items([('mike', 18), ('lisa', 15), ('joe', 12)])
if 'lisa' in dt.keys():
print(dt[lisa])
for i in dt.items():
print(i[0], i[1], end=',') # 这也是遍历字典的方式
for j, k in dt.items():
print(j, k, end=',')
集合
- 元素类型可不同,但必须是不可变的,而且不可变元素若包含可变元素(如包含列表的元组),也不能作为集合的元素。
- 元素不重复
- 可以增删元素
- 作用是快速判断某元素是否属于这个集合(用 i n in in)
其它类型向集合转换
a = set([1, 1, 2, 2, 'hi', True, 2.2])
print(a, end='') # >>{1, 2, 2.2, 'hi'} 自动去重
b = set('banana') # >>{'b', 'a', 'n'}
集合常用函数
a.add('wa') # 添加元素
a.remove('wa') # 移除,不存在则报错
a.update([1, 2]) # 序列的元素加入集合
b = a.copy() # 浅拷贝
a.clear() # 清空
集合的运算
设 A , B A,\,B A,B是集合,存在运算: A ∩ B , A ∪ B , A − B , A ⊕ B A\cap B,\,A\cup B,\,A-B,\,A\oplus B A∩B,A∪B,A−B,A⊕B
其中,
A − B = A ∩ ∼ B A-B=A\,\cap\sim B A−B=A∩∼B
A ⊕ B = ( A − B ) ∪ ( B − A ) = A ∪ B − A ∩ B A\oplus B = (A-B)\cup(B-A)=A\cup B-A\,\cap B A⊕B=(A−B)∪(B−A)=A∪B−A∩B
python依次对应:
a = {1, 2, 3}
b = {2, 3, 5}
a & b == {2, 3}
a | b == {1, 2, 3, 5}
a - b == {1}
a ^ b == {1, 5}
增强型运算仍然可以使用, 如
a ^= {1, 3, 5}
a &= {1, 8}
a ∈ A : a \in A: a∈A:
True == 1 in a
集合间存在关系: A = B , A ≠ B , A ⊆ B , A ⊂ B A=B,\,A\neq B,\,A\subseteq B,\,A\subset B A=B,A=B,A⊆B,A⊂B
python依次对应:
a == b
a != b
a <= b
a < b
文件读写
打开文件
用open()函数打开文件,与C/C++类似。
file = open('D:\\text.txt', 'r+')
有以下几种模式可选
- ‘r’——读
- ‘rb’——二进制读
- ‘w’——写,原先的内容会被覆盖
- ‘wb’——二进制写
- ‘a’——写,若文件不存在则创建
- ‘r+’——读写
- ‘rb+’——二进制读写
除‘a’模式外,其他模式下若文件不存在则抛出异常。
读取内容
若某文本如下:
hi!
How are you?
多行读取:
f1 = file.readlines() # f1是列表,一行就是一个元素(带换行符)
print(f1) # >>['hi!\n', 'How are you?']
单行读取:
while True:
data = file.readline()
print(data)
if data == '':
break
写入内容
使用write()函数写入内容。
file.write('How\'s it going?\n')
关闭文件
file.close()
文件编码
常见编码有gbk(ANSI), utf-8两种,打开文件时编码不对则不能正确读取文件。
Linux,MacOS使用utf-8缺省编码,即不指定时默认的编码规则。
.py文件必须存为utf-8编码,如果非要存为ANSI格式,文件开头要写:
#coding=gbk
open()函数可以指定编码规则,若不指定则用系统的缺省编码。
file = open('D:\\text.txt', 'r+', encoding='utf-8')
file = open('D:\\text.txt', 'r+', encoding='gbk')
文件路径
相对路径
open('readme.txt', 'r') # 文件在当前文件夹下
open('tmp/readme.txt', 'r') # 文件在当前文件夹的tmp文件夹里
open('../readme,txt', 'r') # 在上一级文件夹里
open('../../readme,txt', 'r') # 在上二级文件夹里
open('/tmp/tmp1/readme.txt', 'r') # 在当前盘符的根文件夹下的tmp/tmp1里
相对路径是以当前文件的位置为参考位置的。一般来说,.py文件所在的文件夹,就是程序运行时的“当前文件夹”。
绝对路径
当文件路径包含盘符时,目标文件位置的确定不依赖于当前文件。
open('D:/tmp/project0/readme.txt', 'w')
文件夹操作
os和shutil库中有一些操作文件、文件夹的函数。
os.chdir(x) # 程序当前文件夹设置为x
path = os.getcwd() # 返回程序当前文件夹路径
fileList = os.listdir(x) # 返回列表,列表中是x中所有文件和子文件夹的名字
os.mkdir(x) # 创建文件夹x
os.path.exists(x) # 判断文件/文件夹是否存在
os.path.getsize(x) # 获取文件夹大小(字节)
os.path.isfile(x) # 判断是不是文件
os.path.isdir(x) # 判断是不是文件夹
os.remove(x) # 删除文件x
os.rmdir(x) # 删除空文件夹x,必须为空才能删除成功
os.rename(x, y) # 重命名文件,同时还可以移动文件
shutil.copyfile(x, y) # 拷贝文件x到文件y,若y存在则会被覆盖
综合1:连根删除一个非空文件夹
import os
def delDir(path):
list0 = os.listdir(path)
for i in list0:
i = path + '/' + i
if os.path.isfile(i):
os.remove(i)
else:
delDir(i)
os.rmdir(path)
综合2:获取一个文件夹的大小
import os
def getDirSize(path):
size = 0
list0 = os.listdir(path)
for i in list0:
i = path + '/' + i
if os.path.isfile(i):
size += os.path.getsize(i)
else:
size += getDirSize(i)
return size
命令行运行程序
在命令行窗口输入:
python xxx.py
就能运行程序。
但是有的程序需要提供外部命令行参数。比如合成A.txt和B.txt的程序,用命令行运行:
python xxx.py 'A.txt' 'B.txt'
命令行参数存在sys.argv里。写python程序时通过sys.argv读取命令行参数。
sys.argv[0] == 'A.txt'
sys.argv[1] == 'C.txt'
面向对象程序设计
类的基础构造
python中的类定义实例:
import math
class Circle:
def __init__(self, x, y, radius): # 每个类必有的构造函数__init__
self.x, self.y, self.radius = x, y, radius # 成员在此处定义并赋值
def getSquare(self):
return math.pi * self.radius * self.radius
def getCir(self):
return 2 * math.pi * self.radius
c = Circle(4, 6, 1) # 生成对象
print(c.getSquare())
类的方法若引用非静态属性、方法要有self作为参数,self指向对象自己。引用对象的属性时用 self.参数 引用。
实际上,python除了整数常量以外,复数、小数、字符串、元组、集合、字典的常量都是对象。各种库(模块)都是由类构成。
类的比较
内置类的比较
- x == y——__eq__(equal)
python中所有的类都有__eq__方法。x==y的值,就是x.__eq__(y)的值,若无定义则是y.__eq__(x)的值,若二者都无定义,则x == y也没有定义。下面也同理,
- x < y——__lt__(less than)
- x <= y——__le__(less or equal)
- x > y——__gt__(greater than)
- x >= y——__ge__(greater or equal)
因为整数常量不是对象,所以这不适用于整数常量。
自定义类的比较
- 默认情况下,自定义类的__eq__功能是判断两个对象的id是否相同。也就是对于一个没有重写__eq__的自定义类,x == y和 x is y是等价的。
- 默认情况下,自定义类的__lt__、__le__、__gt__、__ge__都被设为None,于是默认两个对象不能用<, <=, >, >=比较大小。
但是通过重写这些方法,可让自定义类的对象可以用<, <=, >, >=比较大小:
def __eq__(self, other):
return self.x == other.x and self.y == other.y and self.radius == other.radius
def __ge__(self, other):
return self.radius >= other.radius
c1 = Circle(2, 3, 5)
c2 = Circle(2, 3, 5)
c3 = Circle(1, 1, 4)
print(c1 == c2) # >>True
print(c1 >= c3, end='') # >>True
继承和派生
class Cylinder(Circle): # 继承,小括号里是父类
def __init__(self, x, y, r, h):
Circle.__init__(x, y, r)
self.height = h
def getVolume(self):
return Circle.getSquare() * self.height
def getSuperficialArea(self):
return 2 * Circle.getSquare() + self.height * Circle.getCir()
其实所有类都是object类的派生类,因此具有object类的所有方法:
print(dir(Cylinder)) # >>['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
静态属性和方法
- 静态属性在类中定义,只有一个,被类创建出的所有对象共享。
- 静态方法不作用于具体对象,不能访问非静态属性。
- 静态属性和静态方法的存在是为了高度封装,减少全局变量和全局函数的使用。
class Activity:
total = 0 # 静态属性
spendSum = 0 # 静态属性
def __init__(self, time, loc, spend):
self.time, self.loc, self.spend = time, loc, spend
def conduct(self):
Activity.total += 1 # 区别于非静态属性,静态属性用类名调用
Activity.spendSum += self.spend
@staticmethod # 静态方法
def printStatics():
print(Activity.total, Activity.spendSum)
a1 = Activity('2021.5.23', 'abc square', 1000)
a2 = Activity('2022.1.1', 'aaa square', 500)
a1.conduct()
Activity.printStatics() # >>1 1000 区别于非静态方法用对象名调用,静态方法用类名调用
a2.conduct()
Activity.printStatics() # >>2 1500
建立可哈希对象
内置类的哈希
x可哈希,即hash(x)有定义。
对于整型常量, hash(x) = x
其他:hash(x) = x.__hash__() [object类有这个方法]
只有可哈希的对象才能作为字典的键、集合的元素。列表、集合、字典的__hash__被设为None,因此不可哈希,不能作为字典的键或集合的元素。
eg(计算一些可哈希对象的哈希值):
print(3.14.__hash__()) # >> 322818021289917443
print(2+3j.__hash__()) # >> 3000011
print('hello, world!'.__hash__()) # >> -3904558826133324077
print((1, 2, True, 'a', 3.1).__hash__()) # >> -5065489019475364519
字典和集合的底层实现是哈希表:
元素都放在“槽”里,“槽”编号即元素哈希值hash(x). 哈希碰撞的元素存在同一个“槽"里。
找dt[x]时,先计算hash(x), 得到“槽’'编号;若槽空,则不存在;若槽不空,把槽里面存在的元素的key与x一一比对,吻合则提取出value,都不吻合则不存在。
自定义类的哈希
默认自定义类的对象的哈希是根据对象id来计算。即对某对象a,hash(a) = hash(id(a))
即只要a is b == False,a和b就能共存于一个集合,或作为字典里不同元素的键。
可以重写__hash__使自定义对象的哈希值与对象的属性绑定。
def __hash__(self):
return self.loc.__hash__()
如果重写了__eq__但没有重写__hash__,此时__hash__会自动设置成None,变得不可哈希了。因此只有重写了__eq__但没有重写__hash__的自定义类是不可哈希的。