字符串
转移字符与’\’
在\
后面还可以跟一个八进制或者十六进制数来表示字符,例如\141
和\x61
都代表小写字母a
,前者是八进制的表示法,后者是十六进制的表示法。也可以在\
后面跟Unicode字符编码来表示字符,例如\u9a86\u660a
代表的是中文“骆昊”。
>>> s1 = '\141\142\143\x61\x62\x63'
>>> s2 = '\u9a86\u660a'
>>> print(s1, s2)
abcabc
骆昊
如果不希望字符串中的\
表示转义,我们可以通过在字符串的最前面加上字母r
来加以说明,即使用原生字符
>>> s1 = r'\'hello, world!\''
>>> s2 = r'\n\\hello, world!\\\n'
>>> print(s1, s2, end='')
\'hello, world!\' \n\\hello,wolrd!\\\n
字符串处理函数
使用eval()函数执行字符串中的表达式
>>> eval('2')
2
>>> eval('pow(2,2)')
4
>>> x = 3
>>> eval('3 * x')
9
Unicode编码
>>> "1 + 1 = 2 " + chr(10004)
'1 + 1 = 2 ✔'
>>> "这个字符♉的Unicode值是: " + str(ord('♉'))
'这个字符♉的Unicode值是: 9801'
>>> for i in range(12):
print(chr(9800 + i), end="")
♈♉♊♋♌♍♎♏♐♑♒♓
字符串一众方法
str1 = 'hello, world!'
# 通过内置函数len计算字符串的长度
print(len(str1)) # 13
# 获得字符串首字母大写的拷贝
print(str1.capitalize()) # Hello, world!
# 获得字符串每个单词首字母大写的拷贝
print(str1.title()) # Hello, World!
# 获得字符串变大写后的拷贝
print(str1.upper()) # HELLO, WORLD!
# 从字符串中查找子串所在位置,是下标位置+1
print(str1.find('or')) # 8
print(str1.find('shit')) # -1
# 与find类似但找不到子串时会引发异常
# print(str1.index('or'))
# print(str1.index('shit'))
# 检查字符串是否以指定的字符串开头
print(str1.startswith('He')) # False
print(str1.startswith('hel')) # True
# 检查字符串是否以指定的字符串结尾
print(str1.endswith('!')) # True
# 将字符串以指定的宽度居中并在两侧填充指定的字符
print(str1.center(50, '*'))
# 将字符串以指定的宽度靠右放置左侧填充指定的字符
print(str1.rjust(50, ' '))
str2 = 'abc123456'
# 检查字符串是否由数字构成
print(str2.isdigit()) # False
# 检查字符串是否以字母构成
print(str2.isalpha()) # False
# 检查字符串是否以数字和字母构成
print(str2.isalnum()) # True
str3 = ' jackfrued@126.com '
print(str3)
# 获得字符串修剪左右两侧空格之后的拷贝
print(str3.strip())
字符串的格式化
%
方法>>> a, b = 5, 10
>>> '%d * %d = %d' % (a, b, a * b)
'5, 10, 50'
format
方法>>> a, b = 5, 10
>>> '%d * %d = %d'.format(a, b, a * b)
'5, 10, 50'
f-string方法 ```python x = ‘haha’ y = ‘halo’ print(f’{y} {x}’)
输出 halo haha
- 在字符串前加 `f` ,语法糖<br />
```python
>>> a, b = 5, 10
>>> f'{a} * {b} = {a*b}'
'5, 10, 50'
{}槽内部对格式化的配置方式
列表
列表排序
list1 = ['orange', 'apple', 'zoo', 'internationalization', 'blueberry']
list2 = sorted(list1) # ['apple', 'blueberry', 'internationalization', 'orange', 'zoo']
# sorted函数返回列表排序后的拷贝不会修改传入的列表
# 函数的设计就应该像sorted函数一样尽可能不产生副作用
list3 = sorted(list1, reverse=True) # ['zoo', 'orange', 'internationalization', 'blueberry', 'apple']
# 通过key关键字参数指定根据字符串长度进行排序而不是默认的字母表顺序
list4 = sorted(list1, key=len) # ['zoo', 'apple', 'orange', 'blueberry', 'internationalization']
# 给列表对象发出排序消息直接在列表对象上进行排序
list1.sort(reverse=True) # ['zoo', 'orange', 'internationalization', 'blueberry', 'apple']
生成式和生成器
- 下面的代码使用生成式语法创建列表
除了上面的生成器语法,还有一种通过>>> [x for x in range(1, 10)]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> [x + y for x in 'ABCDE' for y in '1234567']
['A1',
'A2',
'A3',
'A4',
'A5',
'A6',
'A7',
'B1',
...]
# 用列表的生成表达式语法创建列表容器
# 用这种语法创建列表之后元素已经准备就绪所以需要耗费较多的内存空间
>>> f = [x ** 2 for x in range(1, 1000)]
>>> print(sys.getsizeof(f)) # 查看对象占用内存的字节数
9024
# 请注意下面的代码创建的不是一个列表而是一个生成器对象
# 通过生成器可以获取到数据但它不占用额外的空间存储数据
# 每次需要数据的时候就通过内部的运算得到数据(需要花费额外的时间)
f = (x ** 2 for x in range(1, 1000))
>>> print(sys.getsizeof(f)) # 相比生成式生成器不占用存储数据的空间
120
>>> type(f)
generator
>>> for val in f:
print(val)
...
yield
关键字将一个普通函数改造成生成器函数的方式:比如是生成斐波那契数列的生成器```python def fib(b): a, b = 0, 1 for _ in range(n):
a, b = b, a + b
yield a
def main(): for val in fib(20): print(val)
if name == ‘main‘: main()
- 关于详细的 `yield` ,看链接 [https://www.runoob.com/w3cnote/python-yield-used-analysis.html](https://www.runoob.com/w3cnote/python-yield-used-analysis.html)
<a name="ZEnU8"></a>
# 元组
- 可以保存**类型不同**的多种元素
- 元组中的元素**不能修改**
```python
# 定义元组
t = ('骆昊', 38, True, '四川成都')
# 获取元组中的元素
print(t[0]) # '骆昊'
# 遍历元组中的值
for member in t:
print(member)
# 重新给元组赋值
# t[0] = '王大锤' # TypeError
# 变量t重新引用了新的元组原来的元组将被垃圾回收
t = ('王大锤', 20, True, '云南昆明')
print(t)
# 将元组转换成列表
person = list(t)
print(person)
# 列表是可以修改它的元素的
person[0] = '李小龙'
person[1] = 25
print(person)
# 将列表转换成元组
fruits_list = ['apple', 'banana', 'orange']
fruits_tuple = tuple(fruits_list)
print(fruits_tuple)
我们已经有了列表这种数据结构,为什么还需要元组这样的类型呢?
- 元组中的元素是无法修改的,事实上我们在项目中尤其是多线程环境(后面会讲到)中可能更喜欢使用的是那些不变对象(一方面因为对象状态不能修改,所以可以避免由此引起的不必要的程序错误,简单的说就是一个不变的对象要比可变的对象更加容易维护;另一方面因为没有任何一个线程能够修改不变对象的内部状态,一个不变对象自动就是线程安全的,这样就可以省掉处理同步化的开销。一个不变对象可以方便的被共享访问)。所以结论就是:如果不需要对元素进行添加、删除、修改的时候,可以考虑使用元组,当然如果一个方法要返回多个值,使用元组也是不错的选择。
- 元组在创建时间和占用的空间上面都优于列表。我们可以使用
sys
模块的getsizeof
函数来检查存储同样的元素的元组和列表各自占用了多少内存空间,这个很容易做到。我们也可以在ipython
中使用魔法指令%timeit
来分析创建同样内容的元组和列表所花费的时间。下图为我在Win10上面的测试结果
集合
Python中的集合跟数学上的集合是一致的,不允许有重复元素,而且可以进行交集、并集、差集等运算。
创建和使用集合
- 使用有重复元素的列表或者元组初始化一个元组,会自动去重
# 创建集合的字面量语法
set1 = {1, 2, 3, 3, 3, 2} # invalid syntax
print('Length =', len(set1))
# 创建集合的构造器语法(面向对象部分会进行详细讲解)
set2 = set(range(1, 10))
set3 = set((1, 2, 3, 3, 2, 1)) # 自动去重
# 创建集合的推导式语法(推导式也可以用于推导集合)
set4 = {num for num in range(1, 100) if num % 3 == 0 or num % 5 == 0}
- 使用有重复元素的列表或者元组初始化一个元组,会自动去重
向集合添加和删除元素 ```python set1.add(4) #在末尾添加 set2.update([11, 12]) set2.discard(5) # 和remove一样,删除元素5 if 4 in set2: set2.remove(4) set1.clear() # 清空 set1.pop() # 随机返回并删除一个元素
- 集合交、并、补、差等运算
```python
# 集合的交集、并集、差集、对称差运算
print(set1 & set2)
# print(set1.intersection(set2))
print(set1 | set2)
# print(set1.union(set2))
print(set1 - set2)
# print(set1.difference(set2))
print(set1 ^ set2)
# print(set1.symmetric_difference(set2))
# 判断子集和超集
print(set2 <= set1)
# print(set2.issubset(set1))
print(set3 <= set1)
# print(set3.issubset(set1))
print(set1 >= set2)
# print(set1.issuperset(set2))
print(set1 >= set3)
# print(set1.issuperset(set3))
字典
# 创建字典的字面量语法
scores = {'骆昊': 95, '白元芳': 78, '狄仁杰': 82}
mapping = dict(zip(range(5), reverses(range(5))))
# 创建字典的构造器语法
items1 = dict(one=1, two=2, three=3, four=4)
# 通过zip函数将两个序列压成字典
items2 = dict(zip(['a', 'b', 'c'], '123'))
# 创建字典的推导式语法
dict_com = {val : index for index, val in enumerate([1,2,3,4[)}
items3 = {num: num ** 2 for num in range(1, 10)}
# 通过键可以获取字典中对应的值
print(scores['骆昊'])
# 对字典中所有键值对进行遍历
for key in scores:
print(f'{key}: {scores[key]}')
# 更新字典中的元素
scores['诸葛王朗'] = 71
scores.update(冷面=67, 方启鹤=85) # 如果update的键在原字典中不存在,则清空后加入该新的键
练习
练习1:在屏幕上显示跑马灯文字
import os
import time
def main():
content = '北京欢迎你为你开天辟地…………'
while True:
# 清理屏幕上的输出
os.system('cls') # os.system('clear')
print(content)
# 休眠200毫秒
time.sleep(0.2)
content = content[1:] + content[0]
if __name__ == '__main__':
main()
练习2
设计一个函数产生指定长度的验证码,验证码由大小写字母和数字构成
import random
def generate_code(code_len=4):
"""
生成指定长度的验证码
:param code_len: 验证码的长度(默认4个字符)
:return: 由大小写英文字母和数字构成的随机验证码
"""
all_chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
last_pos = len(all_chars) - 1
code = ''
for _ in range(code_len):
index = random.randint(0, last_pos)
code += all_chars[index]
return code
练习3:设计一个函数返回给定文件名的后缀名
def get_suffix(filename, has_dot=False):
pos = filename.rfind('.')
if 0 < pos < len(filename) - 1:
index = pos if has_dot else pos + 1
return filename[index:]
else:
return ""
综合案例:井字棋游戏
import os
def print_board(board):
print(board['TL'] + '|' + board['TM'] + '|' + board['TR'])
print('-+-+-')
print(board['ML'] + '|' + board['MM'] + '|' + board['MR'])
print('-+-+-')
print(board['BL'] + '|' + board['BM'] + '|' + board['BR'])
def main():
init_board = {
'TL': ' ', 'TM': ' ', 'TR': ' ',
'ML': ' ', 'MM': ' ', 'MR': ' ',
'BL': ' ', 'BM': ' ', 'BR': ' '
}
begin = True
while begin:
curr_board = init_board.copy()
begin = False
turn = 'x'
counter = 0
os.system('cls')
print_board(curr_board)
while counter < 9:
move = input('轮到%s走棋, 请输入位置: ' % turn)
if curr_board[move] == ' ':
counter += 1
curr_board[move] = turn
if turn == 'x':
turn = 'o'
else:
turn = 'x'
os.system('cls')
print_board(curr_board)
choice = input('再玩一局?(yes|no)')
begin = choice == 'yes'
if __name__ == '__main__':
main()