【Python】重拾Python:细节与特性记录

重拾Python:细节与特性记录

Day 1

  1. 字符串的常用操作方法有查找、修改和判断三大类。
  2. 条件表达式(也称为三元操作符):c = a if a > b else b
    条件是 a > b,如果条件为真,则 c 的值为 a,否则 c 的值为 b。
  3. 如果列表append()追加的数据是一个序列,则追加整个序列到列表;使用extend()可以将序列的数据逐一添加到列表。
  4. 如果定义的元组只有一个数据,那么这个数据后面也好添加逗号,否则数据类型为唯一的这个数据的数据类型。
  5. 如果元组里面有列表,修改列表里面的数据则是支持的:tuple2[2][0] = 'aaaaa'
  6. 集合可以去掉重复数据,通过set()类型转换可以快速完成列表去重。
  7. 创建集合使用{}或set(), 但是如果要创建空集合只能使用set()
  8. 字典和集合不支持下标; 不可变数据类型:字符串与元组。
  9. 列表和集合的pop()方法:删除指定下标的元素并返回该元素(默认是删除最后的元素)。
  10. 使用enumerate() 函数用于在迭代过程中同时获取元素和它们的索引。
  11. 字典推导式:快速合并列表为字典或提取字典中目标数据。
## 需求:提取上述电脑数量大于等于200的字典数据
count1 = {key: value for key, value in counts.items() if value >= 200}
  1. 字典的key查找有两种:
dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
print(dict1['name'])  # Tom,key值不存在则报错
print(dict1.get('id', 110))  # 110,key值不存在则返回第二个参数(默认值None)

Day 2

  1. 在Python中,函数必须先定义后使用。
  2. 定义函数时同时定义了接收用户数据的参数a和b,a和b是形参;调用函数时传入了真实的数据10 和 20,真实数据为实参
  3. 函数的嵌套调用:如果函数A中,调用了另外一个函数B,那么先把函数B中的任务都执行完毕之后才会回到上次 函数A执行的位置。
  4. 如何在函数体内部修改全局变量?函数体内使用global a # global 关键字声明a是全局变量。
  5. return可以退出当前函数,导致return下方的代码不执行。(一个函数出现两个return行,只执行第一个return)
  6. 一个函数有多个返回值,书写为return a, b。注意:return a, b写法,返回多个数据的时候,默认是元组类型’()'。
  7. return后面可以连接列表、元组或字典,以返回多个值。
  8. 以位置参数调用函数时根据函数定义的参数位置来传递参数。传递和定义参数的顺序及个数必须一致。
  9. 以关键字参数调用函数,通过“键=值”形式加以指定,没有参数的顺序需求。
  10. 函数定义调用时,所有位置参数必须出现在默认参数前。
  11. 不定长参数:位置包裹传递(组包为元组类型)、关键词包裹传递(组包为字典类型)
// 位置包裹传递
def user_info(*args):
""" 传进的所有参数都被args变量收集,它会根据传进参数的位置合并为一个args元组(tuple)"""
    print(args)	
#### ('TOM',)
user_info('TOM')
#### ('TOM', 18)
user_info('TOM', 18)


// 关键词包裹传递
def user_info(**kwargs):
    print(kwargs)
#### {'name': 'TOM', 'age': 18, 'id': 110}
user_info(name='TOM', age=18, id=110) 

  1. 有组包就有拆包,字典的拆包和元组拆包有不同
def return_num():
    return 100, 200
//元组拆包
num1, num2 = return_num()
print(num1)  # 100
print(num2)  # 200

//字典拆包
dict1 = {'name': 'TOM', 'age': 18}
a, b = dict1
#### 对字典进行拆包,取出来的是字典的key
print(a)  # name
print(b)  # age
print(dict1[a])  # TOM
print(dict1[b])  # 18
  1. 在python中,值是靠引用来传递来的。可以用 id()判断两个变量是否为同一个值的引用。 我们可以将 id值理解为那块内存的地址标识
  2. 可变类型与不可变类型是指:数据能够直接进行修改,如果能直接修改那么就是可变,否则是不可变.
可变类型不可变类型
列表、字典、集合整型、浮点型 、字符串、元组

比如:int计算前后id值会不同,而列表计算前后id值相同。

  1. 在 Python 中,字典的键必须是可哈希的数据类型。可哈希的数据类型是指其值在其生命周期内不可变的数据类型:
  • 数字类型(int、float、complex)
  • 字符串(str)
  • 元组(tuple)
# 字符串作为键
my_dict1 = {'name': 'Tom', 'age': 20}
key = 'name'
value = my_dict[key]  # value 等于 'Tom',这里的 key 是一个变量名


# 整数作为键
my_dict2 = {1: 'apple', 2: 'banana'}
value = my_dict2[1]  # value 等于 'apple'


# 元组作为键
my_dict3 = {('a', 'b'): 'apple', ('c', 'd'): 'banana'}
value = my_dict3[('a', 'b')]  # value 等于 'apple'

  1. 递归的特点:
  • 函数内部自己调用自己;
  • 必须有出口
  1. Lambda 表达式:一种匿名函数,lambda arguments: expression
    其中:
  • arguments 是一个逗号分隔的参数列表,类似于函数的参数列表。
  • expression 是一个简单的表达式,表示函数的返回值。
"""Lambda 表达式也可以与其他函数组合使用"""
my_list = [('a', 3), ('b', 1), ('c', 2)]
sorted_list = sorted(my_list, key=lambda x: x[1])
print(sorted_list)  # 输出 [('b', 1), ('c', 2), ('a', 3)]

  1. 高阶函数
    把函数作为参数传入,这样的函数称为高阶函数,高阶函数是函数式编程的体现。函数式编程就是指这种高度抽象的编程范式。
def sum_num(a, b, f):
    return f(a) + f(b)

result = sum_num(-1, 2, abs)	# abs()函数可以完成对数字求绝对值计算
print(result)  # 3

内置高阶函数:

  • map(func, lst),将传入的函数变量func作用到lst变量的每个元素中
  • reduce(func,lst),其中的func必须是接受两个参数的。而每次func计算的结果继续和序列的下一个元素做累积计算。
  • filter(func, lst),用于过滤序列, 过滤掉不符合条件的元素, 返回一个 filter 对象(序列)。
  1. 推导式
  • 列表推导式
list1 = [i for i in range(10)]

list1 = [i for i in range(0, 10, 2)]
list2 = [i for i in range(10) if i % 2 == 0]

list3 = [(i, j) for i in range(1, 3) for j in range(3)]
print(list1) ##[(1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
  • 字典推导式
dict1 = {i: i**2 for i in range(1, 5)}
print(dict1)  ## {1: 1, 2: 4, 3: 9, 4: 16}

***快速合并两列表的内容为字典***
list1 = ['name', 'age', 'gender']
list2 = ['Tom', 20, 'man']
dict1 = {list1[i]: list2[i] for i in range(len(list1))}
print(dict1)

counts = {'MBP': 268, 'HP': 125, 'DELL': 201, 'Lenovo': 199, 'acer': 99}
## 需求:提取上述电脑数量大于等于200的字典数据
count1 = {key: value for key, value in counts.items() if value >= 200}
print(count1)  ## {'MBP': 268, 'DELL': 201}
  • 集合推导式
list1 = [1, 1, 2]
set1 = {i ** 2 for i in list1}
print(set1)  ## {1, 4}

Day 3

  1. 文件操作的作用
    思考:什么是文件?文件操作包含什么?
    答:打开、关闭、读、写、复制…
    思考:文件操作的的作用是什么?
    答:读取内容、写入内容、备份内容…

总结:文件操作的作用就是把一些内容(数据)存储存放起来,可以让程序下一次执行的时候直接使用,而不必重新制作一份,省时省力。

#### 1. 打开文件
f = open('test.txt', 'w')
#### 2.文件写入
f.write('hello world')
#### 3. 关闭文件
f.close()

打开文件模式:

  • r(read)、w(write)、a(append)
  • ‘b’ 以二进制格式打开;‘+’ 完善读写
  • w 和 a 模式:如果文件不存在则创建该文件;如果文件存在,w模式先清空再写入,a模式直接末尾追加。
  • r 模式:如果文件不存在则报错
  1. 面向对象
    对象属性既可以在类外面添加和获取,也能在类里面添加和获取。
  2. 类与继承在Python中,__xx__()的函数叫做魔法方法,指的是具有特殊功能的函数。
  • __init__()初始化。在创建一个对象时默认被调用,不需要手动调用。
  • __str__()输出对象信息。当使用print输出对象的时候,默认打印对象的内存地址。如果类定义了__str__方法,那么就会打印从在这个方法中 return 的数据。
  • __del__()删除对象时调用
  1. 类与继承
    如果一个类显式地继承自内置类型 object,或者它是内置类型的子类,那么它就是新式类。新式类支持新的特性如描述符、属性等,并且在多重继承时采用广度优先搜索的方式。
# 父类A
class A(object):
    def __init__(self):
        self.num = 1
    def info_print(self):
        print(self.num)
# 子类B
class B(A):
    pass
result = B()
result.info_print()  # 1
  • 在Python中,所有类默认继承object类,object类是顶级类或基类;其他子类叫做派生类。
  • 当一个类有多个父类的时候,对于同名属性和方法:默认使用第一个父类。(多继承时)
  • 子类和父类具有同名属性和方法,默认使用子类的同名属性和方法。(子类重写时)
  • 调用父类方法,但若需要调用到的也是父类的属性时,必须在调用方法前调用父类的初始化__init__()。
  1. super() 可以自动查找父类。调用顺序遵循 mro 类属性的顺序。
    super() 函数特别适合于单继承的情况,因为在多继承中,可能存在多个父类,而 super() 默认会根据 MRO 中的顺序,选择第一个父类。对于多继承的情况,如果要明确调用某个特定父类的方法,可以使用 super() 的第二个参数来指定调用的父类。
class A:
    def greet(self):
        print("Hello from class A")

class B(A):
    def greet(self):
        super().greet()
        print("Hello from class B")

class C(A):
    def greet(self):
        super().greet()
        print("Hello from class C")

class D(B, C):
    def greet(self):
        super().greet()
        print("Hello from class D")

d = D()
d.greet()

输出将会是:

Hello from class A
Hello from class C
Hello from class B
Hello from class D

在调用 D 类的 greet 方法时,super() 函数按照 MRO 的顺序找到了下一个类,也就是 B 类,然后 B 类中的 greet 方法调用了 super().greet(),继续按照 MRO 找到了下一个类 C广度优先搜索),最后是类 A。

  1. 私有权限
    在Python中,可以为实例属性和方法设置私有权限,即设置某个实例属性或实例方法不继承给子类。
  • 设置私有权限的方法:在属性名和方法名 前面 加上两个下划线__。·
  • 注意:私有属性和私有方法只能在类里面访问和修改,一般定义函数名get_xx用来获取私有属性,定义set_xx用来修改私有属性值。
  1. 异常
try:
    可能发生错误的代码
except:
    如果出现异常执行的代码

//当捕获多个异常时,可以把要捕获的异常类型的名字,放到except 后,并使用元组的方式进行书写。//
try:
    print(num)
except (NameError, ZeroDivisionError) as result:
    print(result)

注意:

  • 如果尝试执行的代码的异常类型和要捕获的异常类型不一致,则无法捕获异常。
  • 一般try下方只放一行尝试执行的代码。
  • Exception是所有程序异常类的父类。

else表示的是如果没有异常要执行的代码。finally表示的是无论是否异常都要执行的代码,例如关闭文件。

try:
    f = open('test.txt', 'r')
except Exception as result:
    f = open('test.txt', 'w')
else:
    print('没有异常,真开心')
finally:
    f.close()

  1. try: 可以有异常-捕获语句的嵌套,遵循异常的传递。
  2. 自定义异常
    在Python中,抛出自定义异常的语法为raise异常类对象。使用步骤为:自定义异常类(选择继承的Exception),在try语句中设置异常判断的语句,Exception捕获异常类型。
# 自定义异常类,继承Exception
class ShortInputError(Exception):
    def __init__(self, length, min_len):
        self.length = length
        self.min_len = min_len
    # 设置抛出异常的描述信息
    def __str__(self):
        return f'你输入的长度是{self.length}, 不能少于{self.min_len}个字符'

def main():
    try:
        con = input('请输入密码:')
        if len(con) < 3:
            raise ShortInputError(len(con), 3)
    except Exception as result:
        print(result)
    else:
        print('密码已经输入完成')

main()

  1. 模块与模块调用
    自己的文件名不要和已有模块名重复,否则导致模块功能无法使用.
    如果使用from .. import ..from .. import *导入多个模块的时候,且模块内有同名功能。当调用这个同名功能的时候,调用到的是后面导入的模块的功能
  2. 模块定位顺序
    当导入一个模块,Python解析器对模块位置的搜索顺序是:
    1、当前目录
    2、如果不在当前目录,Python则搜索在shell变量PYTHONPATH下的每个目录。
    3、如果都找不到,Python会察看默认路径。UNIX下,默认路径一般为/usr/local/lib/python/
    模块搜索路径存储在system模块的sys.path变量中。变量里包含当前目录PYTHONPATH和由安装过程决定的默认目录
  3. __dict__是 Python 中所有对象都具有的一个字典属性,用于存储对象的属性和对应的值。
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

# 创建一个 Person 对象
person = Person("Alice", 30)

# 使用 __dict__ 属性访问对象的属性
print(person.__dict__)  # {'name': 'Alice', 'age': 30}

# 使用 __dict__ 属性修改对象的属性
person.__dict__["age"] = 40

# 再次查看对象的属性
print(person.__dict__)  # {'name': 'Alice', 'age': 40}

Day 4

  1. 进程之间不共享全局变量;主进程会等待所有的子进程执行结束再结束

创建子进程会对主进程资源进行拷贝,也就是说子进程是主进程的一个副本。之所以进程之间不共享全局变量,是因为操作的不是同一个进程里面的全局变量,只不过不同进程里面的全局变量名字相同而已。

  1. 如果想要主进程退出子进程销毁,可以设置守护主进程或者在主进程退出之前让子进程销毁
  • 守护主进程:守护主进程就是主进程退出子进程销毁不再执行
  • 子进程销毁:子进程执行结束
#导入进程包
import multiprocessing
import time
# 定义进程所需要执行的任务
def task():
    for i in range(10):
        print("任务执行中...")
        time.sleep(0.2)

if __name__ == '__main__':
    # 创建子进程
    sub_process = multiprocessing.Process(target=task)
    # 设置守护主进程,主进程退出子进程直接销毁,子进程的生命周期依赖与主进程
    # sub_process.daemon = True
    sub_process.start()
    time.sleep(0.5)
    print("over")
    # 让子进程销毁
    sub_process.terminate()
    exit()
    # 总结: 主进程会等待所有的子进程执行完成以后程序再退出
    # 如果想要主进程退出子进程销毁,可以设置守护主进程或者在主进程退出之前让子进程销毁

Process创建的实例对象的常用方法:

  • start():启动子进程实例(创建子进程)
  • join():等待子进程执行结束
  • terminate():不管任务是否完成,立即终止子进程
  1. 参数说明
##进程类Thread参数说明
Process([group [, target [, name [, args [, kwargs]]]]])
##线程类Thread参数说明
Thread([group [, target [, name [, args [, kwargs]]]]])

  1. 线程注意点
  • 线程之间执行是无序的,它是由cpu调度决定的 ,cpu调度哪个线程,哪个线程就先执行,没有调度的线程不能执行。
  • 进程之间执行也是无序的,它是由操作系统调度决定的,操作系统调度哪个进程,哪个进程就先执行,没有调度的进程不能执行。
  • 主线程会等待所有的子线程执行结束再结束
  • 线程之间共享全局变量、共享全局变量数据引发的错误问题
  1. 守护主线程(主线程退出-子线程销毁不再执行)
  2. 多线程同时对全局变量操作数据引发错误。
    通过线程同步: 保证同一时刻只能有一个线程去操作全局变量:
  • 线程等待(join):前一个线程执行完成以后代码再继续执行,让其执行第二个线程(等待
  • 互斥锁:对共享数据进行锁定,保证同一时刻只能有一个线程去操作。(抢占
# 创建锁
mutex = threading.Lock()
# 上锁
mutex.acquire()
...这里编写代码能保证同一时刻只能有一个线程去操作, 对共享数据进行锁定...
# 释放锁
mutex.release()

  • 加上互斥锁多任务瞬间变成单任务,性能会下降,也就是说同一时刻只能有一个线程去执行。
  • acquire()release()方法之间的代码同一时刻只能有一个线程去操作。
  • 如果在调用 acquire 方法 的时候 其他线程已经使用了这个互斥锁,那么此时acquire方法会堵塞,直到这个互斥锁释放后才能再次上锁。
  1. 死锁: 一直等待对方释放锁的情景就是死锁。
    避免死锁:在合适的地方释放锁,在return、函数结束前 确保释放锁
  2. 进程和线程的对比的三个方向

关系对比

  • 线程是依附在进程里面的,没有进程就没有线程。
  • 一个进程默认提供一条线程,进程可以创建多个线程。

区别对比

  • 进程之间不共享全局变量,
  • 线程之间共享全局变量,但是要注意资源竞争的问题,解决办法: 互斥锁或者线程同步
  • 创建进程的资源开销要比创建线程的资源开销要大
  • 进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位
  • 线程不能够独立执行,必须依存在进程中
  • 多进程开发比单进程多线程开发稳定性要强

优缺点对比

  • 进程优缺点:
    • 优点:可以用多核
    • 缺点:资源开销大
  • 线程优缺点:
    • 优点:资源开销小
    • 缺点:不能使用多核
  • 17
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值