Python 速通爆肝、列表推导式、生成器、装饰器、生命游戏
阅读原文时间:2023年07月10日阅读:1

列表推导式

把普通的多行for循环压缩成一行代码,这种压缩语法适用于列表、字典、集合等可迭代数据结构(iterables)

out_list = [out_express for out_express in input_list if out_express_condition]

# ^ 学习列表推导式
# @ 旧
evens = []
for i in range(10):
    if i % 2 == 0:
        evens.append(i)
# @ 新
evens = [i for i in range(10) if i % 2 == 0]  # @ if 可有可无
print(evens)

squares = [i**2 for i in range(10)]
print(squares)

# @ 列表推导式的断行 增加可读性
evens = [
    i
    for i in range(10)
    if i % 2 == 0
]

matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
]

flattened = [
    i
    for row in matrix
    for i in row
]

# EXAMPLE 把一个字典的key和value互换
input_dict = {"gender": '男', 'age': 123}
changed = {value: key for key, value in input_dict.items()}
print(changed)

# EXAMPLE 用一个列表的所有单词的首字母生成一个集合
words_list = ['apple', 'pig']
chars = {w[0] for w in words_list}
print(chars)

总结:

  • Python允许在中括号、花括号之间断行,可以对列表推导式断行增加可读性
  • 学会使用列表推导式,使代码简化

参考资料:Python中的列表推导式 - 阿米扎 - 博客园 (cnblogs.com)


赋值

# ^ 多赋值
a = b = c = 1
print(a, b, c)  # 1 1 1
a, b, c = 1, 2, 3
print(a, b, c)  # 1 2 3

# ^ 同步赋值
a, b = 1, 2
a, b = a+b, a
print(a, b)  # 3 1

# ^ 复合赋值运算符
a += 1
a *= 1
a /= 1
a **= 2

切片(替换、插入)

# ^ 列表切片
newArr = [1, 2, 3, 4, 5]
print("倒数第二个元素", newArr[-2])  # 4
# @ [begin:end:step]    begin<=index<end
print(newArr[0:5:2])  # 0 2 4
print(newArr[-2:])  # 4 5
print(newArr[:-2])  # 1 2 3

# IMPORTANT 数组反向、替换列表、插入元素
print(newArr[-1::-1])
# @ 替换前两项
newArr[0:2] = [-1, -2]
print(newArr)  # [-1, -2, 3, 4, 5]
# @ 头部插入元素
newArr[:0] = [-1, 0]
print(newArr)  # [-1, 0, -1, -2, 3, 4, 5]

字符串处理与判断

# ^ 字符串处理
str = '1 2 3,4 5 6 small'
print(str.split())  # ['1', '2', '3,4', '5', '6', 'small']
print(str.split(','))  # ['1 2 3', '4 5 6 small']
print(str.strip('all'))  # 1 2 3,4 5 6 sm
print(str.strip('1 l'))  # 2 3,4 5 6 sma

newArr = ['hello', 'world', 'hello', 'server']
print('_'.join(newArr))  # hello_world_hello_server

# @ 大小写转换
str0 = 'I love you'
print(str0.upper())  # I LOVE YOU
print(str0.lower())  # i love you

# @ ascii 转换  chr/ord
print(chr(65))  # A
print(ord('A'))  # 65

# @ 字符串判断
str0 = 'A我xd爱123你'
print(str0.islower())
print(str0.isupper())
print(str0.istitle())  # ^ 字符串中所有的单词拼写首字母是否为大写,且其他字母为小写
print(str0.isdecimal())
print(str0.isdigit())
print(str0.isnumeric())
print(str0.isalpha())
print(str0.isalnum())  # ^ 数字和字母组合也可以只包含一类
print(str0.isspace())

"""
  isdigit()
  True: Unicode数字,byte数字(单字节),全角数字(双字节)
  False: 汉字数字,罗马数字,小数
  Error: 无 

  isdecimal()
  True: Unicode数字,全角数字(双字节)
  False: 罗马数字,汉字数字,小数
  Error: byte数字(单字节) 

  isnumeric()
  True: Unicode数字,全角数字(双字节),罗马数字,汉字数字
  False: 小数
  Error: byte数字(单字节)
"""

enumerate() 函数

   用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中

enumerate(sequence, start=0)

   sequence -- 一个序列、迭代器或其他支持迭代对象。

   start -- 下标起始位置,默认为0。

newArr = ['hello', 'server', '!']
for i in enumerate(newArr):
    print(i)  # (0, 'hello') (1, 'server') (2, '!') 迭代出元组

for index, value in enumerate(newArr):
    print(index, value)

格式化字符串

print('Hello {} , Hello {}'.format('world', 'server'))  # Hello world , Hello server
print('Hello {1} , Hello {0}'.format('world', 'server'))  # Hello server , Hello world

# @ 选择列表、元组参数
newArr = ['world', 'server', 'google']
print('Hello {0} , Hello {1[2]}'.format('world', newArr))  # Hello world, Hello google

# @ 高级格式化
print('{0[0]:^8} {0[1]:^8} {0[2]:^8}'.format(newArr))  # world    server   google
"""
    :>n 设置字段宽n,左对齐
    :<n 设置字段宽n,右对齐
    :^n 设置字段宽n,居中显示
"""

读写文件

path = os.path.dirname(__file__)

with open(path + r'/data.txt', 'w') as file:
    file.write('Hello World!')

with open(path + r'/data.txt', encoding='utf-8') as file:
    content = file.read()
    print(content)  # Hello World!

参考资料:runoob


global 关键字

g_test = 10

def change():
    g_test = 4
    print(g_test)  # 4

change()
print(g_test)  # 10

def change2():
    global g_test
    g_test = 8
    print(g_test)  # 8

change2()
print(g_test)  # 8

字符串startswith()

# ^ 字符串 startswith 函数,可以用于模糊查找
"""
startswith函数用于检查字符串是否是以指定子字符串开头,如果是则返回 True,否则返回 False.如果参数 beg 和 end 指定值,则在指定范围内检查.
参数介绍
startswith(str, beg,end)
"""

print('helloworld'.startswith('hello'))

类与对象

创建类:class关键字,根据缩进确定类范围

创建对象: 对象 = 类名(变量) 如:t0 = Myclass()

调用类方法:对象名.类方法名(参数) 如:t0.my_function()

Python 里由"__"开始的和结尾的为保留字.

方法__init__是类最重要的方法之一,根据名字可以看出来,表示初始化,

创建类对象的同时会自动调用这个方法,传参给类变量,通过__init__函数是个不错的选择或者说唯一选择.

class Dog():
    name = '狗狗'

# ^ init 函数
    def __init__(self, name):
        self.name = name

    def bark(self):
        print('My name is {}'.format(self.name))

dog = Dog('旺财')
dog.bark()

生成器

在Python中,一边循环一边计算的机制,称为生成器(Generator).

也可以这样理解,生成器就是一个可以自动迭代的对象,类似于for循环,又比for循环灵活.

生成器的好处是解决了存储问题。你需要多少个就给你找几个数,到了这个数我就停下来休息,等你下次想找别的了,我再接着继续找,一样的找到就休息,所以这样我们就不用考虑,这么多数放哪个地方了.

使用过程中不用考虑数据溢出

def get_odd_num(n):
    i = 1
    while i <= n:
        yield i
        i += 2

for i in get_odd_num(10):
    print(i)  # 1 3 5 7 9

# @ 注意一下 next 的用法,逐一调用下一个
x = get_odd_num(10)
print(x.__next__)

装饰器

装饰器给函数名重新赋值,使其指向原始函数的包装板,包装板不仅具备原始函数的所有功能,还添加了新功能

应用场景

  • 大程序的调试不可能在一个模块里几百个函数一个个调试

  • 我想验证某个东西,但不希望在原始函数添加

    ^ 装饰器

    @ 实现计时器功能,注意这里要引入time

    def my_decorator(f):
    """ 装饰器,将一个函数作为参数传递进来,进行包装,然后返回 """
    def wrapper():
    start = time.perf_counter()
    f()
    end = time.perf_counter()
    print("耗时", round(end-start, 2), "秒")
    return wrapper

    def hello():
    i = 0
    while i < 1000000:
    i += 1
    print("Hello World")

    new_hello = my_decorator(hello)
    new_hello()

    IMPORTANT 可以简写为

    @my_decorator # @ 新语法 @+装饰器的名称
    def hello():
    i = 0
    while i < 1000000:
    i += 1
    print("Hello World")


Python Self

self,表示创建的类实例本身,方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。在创建实例的时候,就不能传入空的参数了,必须传入与方法匹配的参数,但self不需要传,Python解释器会自己把实例变量传进去。

只传一个参数

class Car:
    speed = 0
    def drive(self,distance):
        time = distance / self.speed
        print(time)

bike = Car()
bike.speed=60
bike.drive(80)

传两个或以上的参数

class Car:
    speed = 0
    def drive(self,distance,speed):
        time = distance / speed
        print(time)
bike = Car()
bike.drive(80,50)

参考资料:TypeError:takes 2 positional arguments but 3 were given


*args 与 **kwargs

args表示任何多个无名参数,它是一个tuple
**kwargs表示关键字参数,它是一个 dict
并且同时使用args和kwargs时,必须*args参数列要在kwargs前,
像foo(a=1, b=‘2’, c=3, a’, 1, None, )这样调用的话,会提示语法错误“SyntaxError: non-keyword arg after keyword arg”

def foo(*args, **kwargs):
    print ('args = ', args)
    print ('kwargs = ', kwargs)
    print ('---------------------------------------')

if __name__ == '__main__':
    foo(1,2,3,4)
    foo(a=1,b=2,c=3)
    foo(1,2,3,4, a=1,b=2,c=3)
    foo('a', 1, None, a=1, b='2', c=3)

"""
args =  (1, 2, 3, 4)
kwargs =  {} 

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

args =  (1, 2, 3, 4)
kwargs =  {'a': 1, 'c': 3, 'b': 2} 

args =  ('a', 1, None)
kwargs =  {'a': 1, 'c': 3, 'b': '2'}
"""

参考资料:python中*args,**args的区别_陈少野coder的博客


生命游戏

介绍

生命游戏是英国数学家约翰·何顿·康威在1970年发明的细胞自动机。它包括一个二维矩形世界,这个世界中的每个方格居住着一个活着的或死了的细胞。一个细胞在下一个时刻生死取决于相邻八个方格中活着的或死了的细胞的数量。如果相邻方格活着的细胞数量过多,这个细胞会因为资源匮乏而在下一个时刻死去;相反,如果周围活细胞过少,这个细胞会因太孤单而死去。

基本规律

对于网格中的每个位置,计算有多少个邻接位置中有活细胞,包括对角邻接位置,因此一个方块的周围最多有八个活细胞(数值为1的方块),最少为零,规则就是,如果这个方块周围的活细胞数等于三,就繁殖,也就是值变为1,如果这个方块周围的活细胞数少于两个或者大雨三个,则该方块中细胞死亡,值变为0。

代码

matrix2d.py
"""
二维矩阵类
"""

class Matrix2D:
    """ 通用的二维矩阵类 """

    def __init__(self, rows, cols):
        """ 初始化矩阵row行,col列 """
        self.grid = [[0]*cols for _ in range(rows)]
        self.rows = rows
        self.cols = cols

    def get_cell(self, r, c):
        """ 获取单元格(r,c)的值 """
        return self.grid[r][c]

    def set_cell(self, n, *args):
        """ 设置某个位置的值 """
        for r, c in args:
            self.grid[r][c] = n

    def inc_cells(self, *args):
        """ 将任意的单元格 +1 """
        for r, c in args:
            self.grid[r][c] += 1

    def set_all_cells(self, n=0):
        """ 将所有单元格值都设置为 n """
        for i in range(self.rows):
            for j in range(self.cols):
                self.grid[i][j] = n
game.py
"""
生命游戏
"""

import time
from matrix2d import Matrix2D

rows = 5
cols = 5
# 存储图符号的二维数组
life_mat = Matrix2D(rows, cols)
print(dir(life_mat))
# 存储具体数据的二维数组
nc_mat = Matrix2D(rows, cols)
# 初始化
life_mat.set_cell(1, (1, 3), (2, 1), (2, 3), (3, 2), (3, 3))
# 创建边界字符串
border_str = ' _ ' * cols

def get_mat_str(a_mat):
    """ 处理打印字符串 """
    disp_str = ''
    for i in range(rows):
        lst = [get_chr(a_mat, i, j) for j in range(cols)]
        disp_str += ''.join(lst) + '\n'
    return disp_str

def get_chr(a_mat, r, c):
    """ 设置图符号 """
    return ' 1 ' if a_mat.get_cell(r, c) > 0 else ' 0 '

def do_generation():
    """ 打印当前状态并生成下个状态 """
    # 打印当前生命矩阵状态
    print(border_str + '\n' + get_mat_str(life_mat))
    # 把数据全部置0
    nc_mat.set_all_cells(0)

    # 根据图符号矩阵life_mat来给nc_mat赋值

    for i in range(rows):
        for j in range(cols):
            if life_mat.get_cell(i, j):
                # 环绕图像,使有限的二维数组变成没有边界的生命游戏
                im = (i - 1) % rows
                ip = (i + 1) % rows   # 当前行号-/+ 1
                jm = (j - 1) % cols
                jp = (j + 1) % cols   # 当前列号-/+ 1
                # 设置数据量为 1 ,表示有活细胞
                nc_mat.inc_cells((im, jm), (im, j), (im, jp), (i, jm),
                                 (i, jp), (ip, jm), (ip, j), (ip, jp))
    # 根据邻居数量矩阵按规则生成下一代
    for i in range(rows):
        for j in range(cols):
            n = nc_mat.get_cell(i, j)
            if n < 2 or n > 3:      # 死亡现象
                life_mat.set_cell(0, (i, j))
            elif n == 3:            # 繁殖现象
                life_mat.set_cell(1, (i, j))

n = 100
for i in range(n):
    # 循环调用迭代
    do_generation()
    # 设置时间间隔
    time.sleep(1)

代码参考:老表“简说python”