Python快速上手

Python

本文推荐在有其它语言基础的情况下想快速系统上手python的人阅读

注释

class A:
    """
    多行注释例如,多用于类或函数介绍
    """
    pass

def times(s:str, n:int) -> str: #类型注释,希望接收str类型和int类型,str为返回值
    return s * n
def times(s:list[int],n:int = 3) -> list:
    return s * n

变量定义

a = 1
b = "abc"
c = 1.2

print(a)
print(b)
print(c)
#1
#abc
#1.2

解释型语言在变量定义时类型由解释器推断,无需显示定义变量类型

  • 首次使用变量会在内存中划分空间,并初始化值
  • 再次使用变量不再划分空间,修改原空间的值

标识符约定

  1. 只能由数字、字母、_(下划线)组成
  2. 不能以数字开头
  3. 不能是关键字
  4. 严格区分大小写

模块名写法: module_name ; 包名写法: package_name ; 类名: ClassName ; 方法名: method_name ; 异常名: ExceptionName ; 函数名: function_name ; 全局常量名: GLOBAL_CONSTANT_NAME ; 全局变量名: global_var_name ; 实例名: instance_var_name ; 函数参数名: function_parameter_name ; 局部变量名: local_var_name . 函数名,变量名和文件名应该是描述性的,尽量避免缩写,特别要避免使用非项目人员不清楚难以理解的缩写,不要通过删除单词中的字母来进行缩写. 始终使用 .py 作为文件后缀名,不要用破折号

python关键字:

False None True and as assert async await break class continue def del elif else except finally for from global if import in is lambda nonlocal not or pass raise return try while with yield

命名规范

下划线命名法

下划线分割法,多个单词组成的名词,使用小写字母,单词与单词之间使用下划线分开

大驼峰命名法

多个单词组成的名词,每个单词手写字母大写,其余字母小写

小驼峰命名法

第一个单词首字母小写,后面单词首字母大写,其余字母小写

数值类型

数值(Number)

  • 整数 int
  • 浮点型 float
  • 布尔型 bool 可以当作整型对待 True(1),False(0)
  • 复数 complex 固定写法 z = a + bj a是实部 b是虚部 虚数单位只能是j
print(type(1))
print(type(1.2))
print(type(True))
print(type(2 + 3j))

#<class 'int'>
#<class 'float'>
#<class 'bool'>
#<class 'complex'>

检测数据类型方法:type()

字符串类型

单引号(’)双引号(“) 三引号(“””) 都行

三引号如果在多行内容上可以使用

print('abc')
print("abc")
print(
"""abc
efj"""
)
#abc
#abc
#abc
#efj

注释

多行注释用三引号,单行用#号

"""
我是多行注释
不会被执行
"""
#注释

字符串格式化

%格式化

name = "bob"

age = 18

print( "my name is %s" %name)

print( "my name is %s, my age is %d" %(name,age))

# %5d代表5位数,空位用空格补全
print( "my name is %s, my age is %5d" %(name,age))

# %05d 代表5位数用0补全空位
print( "my name is %s, my age is %05d" %(name,age))

score = 92.12

#浮点数默认6位小数,遵循四舍五入原则
print("score is %f" %score)

#设置保留小数位数,遵循四舍五入原则
print("score is %.1f" %score)

#打印%
print("我是 %%" %())

f格式化

name = "bob"
age = 18
print(f"my name is {name}, my age is {age}")

运算符

运算符 描述 实例
+ 10 + 20 = 30
- 10 - 20 = -10
* 10 * 20 = 200
/ 10/20 = 0.5
// 取整 9 // 2 = 4
% 取余 9 % 2 = 1
** 2**3 = 8

输入函数

info = input("请输入信息:")
print(f"你输入了{info}")

关系运算符

运算符 描述
> 大于
< 小于
>= 大于等于
<= 小于等于
== 等于
!= 不等于
and
or
not

if - elif - else

num = int(input("输入一个数字:"))
if num > 10:
    print("输入的数字大于10")
elif num > 5:
    print("输入的数字在 [6,10] 内")
else:
    print("输入的数字小于等于5")

三目运算

a = 2
b = 3
c = 10 if a > b else 0 #三目运算
print(c)

循环

#while 循环
i = 0
while i < 100:
    i+=1
    print(i)
#for循环 for 临时变量 in 可迭代对象
for i in "abcdefj":
    print(i)

#range函数  左闭右开遍历 i输出0-9
for i in range(0,10):
    print(i)
#range(start,stop,step) 以2为步长遍历  
for i in range(0,10,2):
    print(i)
#循环5次,默认0开始
for i in range(5):
    print(i)

字符串常见操作

str1 = "Hello"
str2 = "World"
print(str1 + str2) #HelloWorld
print(str1 * 3) #HelloHelloHello
print(str1 * 3) #HelloHelloHello

#子串判断
print("b" not in(str1)) #True
print("H" in (str1)) #True

#下标取字符
print(str1[0]) #H

切片

指对操作对象截取其中一部分操作

语法:[开始位置:结束位置:步长]

str1 = "Hello"

print(str1[1:4]) #ell
print(str1[0:]) #Hello
print(str1[:4]) #Hell
print(str1[::2]) #Hlo
#为负数代表从右往左取值
print(str1[::-1]) #olleH
print(str1[-1:-6:-2]) #olH

常见方法

str = "abcdefj"

#查找子字符串,返回开始位置下标
print(str.find("abc"))
#查找范围从索引3开始到字符串末尾
print(str.find("abc",3)) #没找到返回-1

#查找子字符串索引位置
print(str.index("def"))

#index()和find()的区别在于没找到会报错
#print(str.index("x"))

#检查字符串是否以某字串开头/结束
print(str.startswith("abc"))
print(str.startswith("def",3))
print(str.endswith("efj"))

#是否大小写检查
print(str.isupper())
print(str.islower())

#修改元素
print(str.replace("abc","___"))
print("abcabcabc".replace("abc","___",2)) #替换次数默认为1

#字符串分割
print("a b c".split())

#首字母大写其余都小写
print(str.capitalize())

#大写转小写
print("Abc".lower())

#小写转大写
print(str.upper())

列表

列表元素类型可以不同

array = ["hi", 12, False , 3.14]

for e in array:
    print(e)

array = array[::-1] #列表也支持切片操作
for e in array:
    print(e)

#整体添加,将要加入对象作为整体加入列表
array.append("abcd") # "abcd"加入列表

#分散添加,将另一个类型中的元素逐一添加 注意:extend必须加可迭代对象
array.extend("abcd") # "a", "b", "c", "d" 加入列表

#在指定位置加入元素,如果指定位置已经有元素,原有元素后移
array.insert(0,"abc")

#修改元素,直接通过下标即可修改
array[0] = 1
print(array[0]) # 1

#查询 in 和 not in
print("abcd" in array)
print(1 not in array)

#删除元素
#del
#del array #删除列表
#print(array) 会报错,列表已经被删除了

del array[0] #删除指定下标数据

#pop
array.pop() #删除指定下标数据,python3默认删除最后一个
array.pop(0) # 指定下标删除
print(array)

#remove
array.remove("hi") #指定值删除 如果值不存在会报错
#默认删除最开始出现的元素
print(array)


li = [2,1,5,0,9,8]
#倒序列表
li.reverse()
print(li)

#sort排序 默认从小到大
li.sort()
print(li)

#列表推导式,化简写法
#语法:  [表达式 for 变量 in 列表]
#[表达式 for 变量 in 列表 if 条件]
li = []
[li.append(i) for i in range(10)]
print(li)

li = []
[li.append(i) for i in range(10) if i % 2 == 0]
print(li)

元组

元组与列表区别在于元组不支持增删改操作

#只有一个元素的时候末尾要加, 如 (1,) 否则会返回唯一一个元素
tua = (1,"abc",False)
print(tua)
print(tua[0])

元组应用在函数的参数和返回值,格式化输出后面的 %(name,age)实际上也是一个元组,还有就是数据不可以被修改的场景

字典

#字典
map = {
    "a" : "value is a",
    1 : "number is 1",
    "a" : "duplex value" # key可以重复,但是会覆盖旧的
}

#根据键名查找值
print(map["a"]);
print(map[1]);


#字典常见操作
print(map.get("a")) #如果键名不存在返回None 与中括号取值直接报错不同

#修改元素/添加元素 ,键名存在就是修改,不存在则是新增
map["a"] = 20
print(map["a"])

#删除元素
# del map 删除整个字典
del map["a"] #删除指定键值对, 键值不存在则报错

map.clear() #清空字典,但是保留字典
print(map)
map["a"] = 1

map.pop("a") #删除指定键值对,键不存在则报错
map["a"] = 1
map.popitem() #默认删除最后一个

#字典求长度
print(len(map))

# 取出键名
keys = map.keys()
#取出值
values = map.values()
#取出键值对,以元组形式
items = map.items()

集合

#集合 集合具有无序性 实现方式涉及哈希表
#定义空集合
set = set()
set = {"a", True, 23}
print(set)

#无序性:不能修改集合中的值 具有唯一性 可以自动去重

#添加元素
set.add("test") #添加一个整体
set.update([2,3,4]) #分散添加 必须是可迭代对象
set.remove("test") #删除指定元素 集合中没有则会报错
set.pop() #默认删除哈希排序后的第一个元素
set.discard(9) #删除指定元素 元素不存在也不会报错

a = {1,2,3,4}
b = {3,4,5,6}
print(a & b) #求交集

s1 = {'a','b'}
s2 = {'c','d'}
print(s1 | s2) #求并集

类型转换

#转换整型
print(int(2.6)) #2 直接去除小数部分
print(int("123")) #123 字符串只能包含数字
print(float("3.14")) #3.14
print(str(2+2j)) #任何类型都能转化为字符串类型
print(eval("10 + 10 * 2")) #用来执行字符串表达式,并执行表达式的值 可以转换 list, dict, tuple
print(type(eval("[1,2,3]"))) #<class 'list'>
print(type(eval("{1:'a',2:'b'}"))) #<class 'dict'>
#eval(): 非常强大,但是不安全

#list(): 将可迭代对象转换为列表
print(list("abcdefj"))
print(list((1,2,3,4)))
print(list({'name':'bob','age':18})) #字典转列表取键名
print(list({'a','b','c'})) #集合转换列表会先去重再转换

深浅拷贝

import copy
#id(): 查看内存地址
#浅拷贝
li1 = [1,2,[1,2,3]]
li2 = copy.copy(li1)
print(li1)
print(li2)
print(id(li1[2]) == id(li2[2])) #True

#深拷贝
li3 = copy.deepcopy(li1)
print(li1)
print(li3)

print(id(li1) == id(li3)) #False

函数

定义

#函数
#定义
def method():
    print("this is a method")
    return 0
# return返回多个值,以元组形式返回给调用者
# 无返回值返回None

result = method() #调用函数 函数必须存在
print(f"function return is {result}")

参数区别

# 必备参数: 传递和定义参数的顺序及个数必须一致
def method(str):
    print(str)

# 默认参数: 为参数提供默认值,调用函数时可不传 (必须在必备参数后)
def add(a = 1,b = 1):
    return a+b
print(add()) #使用默认参数值
print(add(0))
print(add(1,2))

# 可变参数 传入的值的数量可变,可以传一个或多个,以元组形式接收参数
def addAll(*args):
    sum = 0
    for arg in args:
        sum += arg
    return sum

print(addAll(1))
print(addAll(1,2,3,4))

#关键字参数
def func(**kwargs):
    print(type(kwargs)) # <class 'dict'>
    for kwarg in kwargs:
        print(kwarg, kwargs[kwarg])
#作用: 可以扩展函数功能
func(name="bob",age=18) #固定调用格式 key=value形式

嵌套定义

#嵌套定义
def func1():
    print("func1")
    def func2():
        print("func2") #不要在内层函数调用外层函数,会爆栈
    func2()
func1()

全局变量: 函数外部定义的变量,在整个文件中都是有效的

函数优先使用作用域范围内变量,如果没有才去函数外部查找

局部变量: 函数内部定义的变量,从定义位置开始到函数定义结束位置有效

全局变量和局部变量命名相同,在函数内部如何修改全局变量的值?

使用global 关键字,将局部变量声明为全局变量(可以在局部作用域中声明一个全局变量)

nonlocal 关键字,一般在嵌套内层函数使用,用于修改外层函数局部变量值

#global 例子
a = 10
def func():
    global a # 使用global修改全局变量
    a = 20
func()
print(a)

# nonlocal例子
def func2():
    b = 10
    def func3():
        nonlocal b #修改嵌套函数外层的局部变量
        b = 20
    func3()
    print(b)

func2()

匿名函数

add = lambda a,b : a+b
print(add(1,3))

#参数形式
#无参数
func1 = lambda : "result"
print(func1())

#一个参数
func2 = lambda name : f"name is{name}"
print(func2('bob'))

#默认参数
func3 = lambda name,age=18 :(name,age)
print(func3('bob'))
print(func3('bob',12))

#可变长参数
func4 = lambda *args : sum(args)
print(func4(1,2,3))

#关键字参数
func5 = lambda **kwargs :kwargs
print(func5(name='bob',age=18))

内置函数

import builtins
#内置函数
print(dir(builtins)) #内置常量,函数名

#返回绝对值
print(abs(-10))
print(abs(10))

#求和
print(sum({1,2,3}))

#求最小值
print(min(-1,2,3))
#求最大值
print(max(1,2,3))
#绝对值最小值
print(min(-1,-2,-3,5,key=abs))

#将可迭代对象打包为元组
li = [1,2,3]
li2 = ['a','b','c','d']
print(zip(li,li2))
#取出元素
#如果元素个数不一致,按照长度最短的返回
for e in zip(li, li2):
    print(e)
    print(type(e))
print(list(zip(li,li2))) #转换成列表打印

#映射函数
array = [1,2,3,4]
mp = map(lambda a:a+1,array )
#列表化打印
print(list(mp))

#先将对象中的两个元素取出,计算出一个值后保存与第三个值进行计算
from functools import reduce
res = reduce(lambda a,b:a+b,array) #只能接收2参数函数
print(res)

拆包

tuple = (1,2,3,4)
a,b,c,d = tuple # 要求元组内个数与接收对象个数相同
print(a,b,c,d)

array = [6,7,8,9]
a,b,c,d = array
print(a,b,c,d)

a,*b = (1,2,3,4) # 拆包 a = 1 b = [2,3,4] b可变,能接收多个
print(a,b)

异常

抛出异常并捕获异常

def check_number_str(str):
    if str.isdigit():
        print('ok')
    else:
        raise Exception('输入值非法')


str = input('请输入一个自然数:')

try:
    check_number_str(str)
except Exception as e:
    print(e)

模块

含义:在python中一个py文件就是一个模块,里面定义了一些函数和变量,需要的时候可以导入并使用这些模块

执行步骤:

  1. 在python模块加载路径中查找相应的模块文件
  2. 将模块文件编译成中间代码
  3. 执行模块文件中的代码

pip install 模块名 可以安装第三方模块

自定义模块

自己在项目中定义的模块,注意命名要遵循标识符规定,不要与内置模块起冲突

import my_test
#调用模块内变量名
print(my_test.name)
#调用模块函数
my_test.func()

#直接输入功能即可,不需要添加模块名
from my_test import func2
func2()

#将模块内所有内容全部导入,不推荐,命名冲突时易造成错误
from my_test import *
print(name)
func()

#模块起别名
import my_test as mt
print(mt.name)

#功能起别名
from my_test import func as test_func
test_func()

内置全局变量__name__
语法:
if __name__ == "__main__"
作用: 用来控制Python文件在不同场景执行不同逻辑

文件在当前程序执行(自己执行自己): __name__ == "__main__"

__main__中的代码类似本模块私有

文件被当作模块被其它文件导入: __name__ == 模块名

if __name__ == "__main__":
    print("这是my_test")

else:
    print("正在被其它模块调用")

项目结构中的文件夹/目录

作用:

将有联系的模块放到同一个包下,并且在这个文件夹中创建一个名为__init__.py的文件,那么这个文件夹就被称为包,有效避免模块名冲突问题,让结构更清晰

  • 在import包导入时,首先执行__init__.py文件中的代
  • 不建议在init中编写过多代码,尽量保证init的内容简单
  • 包本质上还是模块,包还可以包含包
  • __all__变量:一个列表,可以控制要引入的东西(模块、函数、类等)
# mymodule.py
 
# 导出的名称列表
__all__ = ['function1', 'function2']
 
def function1():
    return "This is function 1."
 
def function2():
    return "This is function 2."
 
def _private_function():
    return "This is a private function."

from mymodule import * 就只会导入__all__中导出的函数

闭包

用于解决在函数外需要获得函数内局部变量的问题,使用嵌套定义解决

闭包前提:

  • 函数嵌套定义
  • 内层函数使用外层函数局部变量
  • 外层函数返回值是内层函数的函数名
def outer(m):
    def inner(n):
        print(f"n + m = {n + m}")
    return inner


func = outer(20)
#调用闭包函数
func(10)

装饰器

装饰器本质上是一个Python函数,它可以让其它函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象

装饰器需要满足以下两点:

  • 不修改原程序或函数的代码
  • 不改变函数或程序调用方法

标准版装饰器实现(闭包实现)

#装饰器
def outer(fn):
    def inner():
        fn()
        # 增加新功能
        print("World")
    return inner

#需要被装饰的函数
def send():
    print("Hello")

#装饰器装饰后的函数
outer(send)()

装饰器模式,遵循对扩展开放,对修改关闭

Python中装饰器的原理就是将原有的函数名重新定义为以原函数为参数的闭包

装饰器语法糖

格式:@装饰器名称

#装饰器
def outer(fn):
    def inner():
        fn()
        # 增加新功能
        print("World")
    return inner

#需要被装饰的函数
@outer
def send():
    print("Hello")


#装饰器装饰后的函数
send()

装饰函数携带参数时,使用语法糖注意被装饰函数也要携带参数,否则会报错

#装饰函数携带参数时
#装饰器
def outer(fn):
    def inner(arg):
        fn()
        # 增加新功能
        print("World")
        print(f"arg is {arg}")
    return inner

#需要被装饰的函数
@outer
def send():
    print("Hello")


#装饰器装饰后的函数
send(123) #必须携带参数,给inner函数使用
#装饰器
def deco1(fn):
    def inner():
        print("装饰器1执行")
        return fn() + "装饰器1应用 "
    return inner
def deco2(fn):
    def inner():
        print("装饰器2执行")
        return fn() + "装饰器2应用 "
    return inner
def deco3(fn):
    def inner():
        print("装饰器3执行")
        return fn() + "装饰器3应用 "
    return inner

@deco1
@deco2
@deco3
def func():
    return "需要增强的函数 "

#装饰器装饰后的函数
print(func())
#装饰器1执行
#装饰器2执行
#装饰器3执行
#需要增强的函数 装饰器3应用 装饰器2应用 装饰器1应用 

多个装饰器语法糖,会依次逐行执行装饰器代码

  • 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
  • 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
  • 局部变量:定义在方法中的变量,只作用于当前实例的类。
  • 实例变量:在类的声明中,属性是用变量来表示的。这种变量就称为实例变量,是在类声明的内部但是在类的其他成员方法之外声明的。
  • 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
  • 实例化:创建一个类的实例,类的具体对象。
  • 方法:类中定义的函数。
  • 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
#类定义
class Student:
    #类变量,类级别共享
    school = "xxx university"

    #构造函数
    def __init__(self, name = "", age = 0):
        self.name = name
        self.age = age

    #重写字符串方法
    def __str__(self):
        return f"name: {self.name} age: {self.age}"

    #实例方法
    def do_home_work(self):
        print(f"{self.name} is doing home work")
    
    # 静态方法
    @staticmethod
    def static_method():
        print('我是一个静态方法')
    
    # 类方法 cls代表类对象本身
    @classmethod
    def class_method(cls, x):
        # 在类方法中可以访问类的属性
        #print(f"Class attribute: {cls.class_attr}")
        print(f"Received value: {x}")

    #析构函数 对象销毁时自动调用 del语句执行时会立即回收该对象
    def __del__(self):
        print(f"{self.name} is destroyed")

print(Student.school)
student = Student("bob",18) #实例化对象
#实例属性只能由对象名访问
student.sex = "male"
print(student.sex)
print(student)
student.do_home_work()
student.static_method()  # 输出:我是一个静态方法
  • 实例方法都要携带__self__参数,一般是约定俗成的,在实例调用方法时会自动传入当前实例,也就是__self__实际上是当前类实例的引用

  • 静态方法是Python中定义在类中的一种特殊方法类型,它不与类的实例绑定,也不与实例的属性直接交互,通常通过 @staticmethod 装饰器来声明。与普通方法和类方法不同,静态方法既不需要传递类对象(cls)也不需要传递实例对象(self)作为第一个参数。

  • 类方法是定义在类中的方法,通过装饰器@classmethod来标识。它的第一个参数是cls(表示类本身),而不是实例对象。类方法可以访问类的属性,并且可以在没有实例的情况下被调用。

封装

在Python中,以下划线 (_) 开头的属性和方法通常被视为内部使用或私有的。而以双下划线 (__)开头并且不以双下划线结尾的属性和方法叫做“名称修饰(name mangling)”。

以单下划线 (_) 开头的属性和方法: 这种命名约定暗示着某个属性或方法是用于内部使用的,但它们并没有强制限制外部访问。这仅仅是一种约定,用来给其他程序员传达关于该属性或方法的使用意图。

以双下划线 ()开头并且不以双下划线结尾的属性和方法: 这种命名约定用于名称修饰,将属性或方法重命名为”_类名属性名”或”_类名__方法名”的形式。这是Python的一种名称重整机制,旨在避免子类意外覆盖父类的属性或方法。这种重命名机制使得属性或方法变得更加唯一,即使在多层继承中也能保持独立性。需要注意的是,这种名称修饰只在类的定义内部起作用,外部无法直接访问。

  • 私有属性:只允在类的内部使用,无法通过对象访问,在属性或方法名前加两个下划线__,隐藏属性实际上时将属性名改为了_类名__属性名,因此访问不到,且无法被import *导入,这种一般是Python中有特殊功能的属性和方法,不要轻易定义
class Student:

    __school = "xxx university"

    def introduce(self):
        print(self.__school)

    def get_school(self):
        return self.__school

    def public_method(self):
        print("这是一个普通方法")
        self.__private_method()

    def __private_method(self):
        print("这是一个私有方法")

student = Student()

#三种访问方法
student.introduce()
print(student.get_school())
print(student._Student__school) #不推荐

student.public_method()
  • 被保护属性/方法
#类定义
class Student:

    _region = "cn"


student = Student()

print(student._region) #不推荐

通过装饰器装饰后的方法访问被保护属性

#类定义
class Student:

    def __init__(self):
        self._region = "cn"

    @property
    def region(self):
        return self._region

    @region.setter
    def region(self, value):
        self._region = value

student = Student()
print(student.region)
student.region = "en"
print(student.region)

类比Java

  • foo: 定义的是特殊方法,一般是系统定义名字 ,类似 init() 之类的。
  • _foo: 以单下划线开头的表示的是 protected 类型的变量,即保护类型只能允许其本身与子类进行访问,不能用于 from module import *
  • __foo: 双下划线的表示的是私有类型(private)的变量, 只能是允许这个类本身进行访问了

继承

即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟”是一个(is-a)”关系(例图,Dog是一个Animal)。

  1. 继承的属性和方法:子类继承了父类的所有属性和方法,包括实例方法、类方法、静态方法以及属性。
  2. 覆盖和扩展:子类可以覆盖父类的属性和方法,也可以添加新的属性和方法来扩展功能。
  3. 私有属性和方法:父类中的私有属性和方法(以双下划线开头,如__private_attr)不会被子类直接继承。它们在子类中是不可见的,但可以通过名称改写(name mangling)来访问。
  4. 构造方法:子类的构造方法(__init__)需要显式调用父类的构造方法,通常是通过super().__init__()来完成,以确保父类的初始化逻辑被正确执行。
  5. 多重继承:Python支持多重继承,子类可以继承多个父类。在这种情况下,属性和方法的继承顺序由方法解析顺序(MRO)决定。
  6. 方法解析顺序(MRO):Python使用C3线性化算法来确定方法解析顺序,这决定了当一个方法在多个父类中定义时,哪一个会被调用。
  7. 属性查找:如果子类和父类有同名的属性或方法,Python会首先在子类中查找,然后按照MRO向上查找。
class Father:
    def method(self):
        print("this is father")
    def play(self):
        print("im playing")
        
class Child(Father):
    def doSomthing(self):
        print("this is child")
    #重写方法
    def play(self):
        print("im playing too")
        super().play() #调用父类方法

child = Child()
child.method()
child.doSomthing()
child.play()
#新式类 object是python提供的顶级父类(基类)
class A(object):
    pass

在python3中,如果一个类没有继承任何类,那么会默认继承object类

多继承

在python中一个子类可以继承多个父类

class Father:
    def __init__(self):
        print("Father init")
class Mother:
    def __init__(self):
        print("Mother init")
#多继承,多个父类属性方法同名时,调用写在前面的父类
class Son(Father, Mother):
    pass

son = Son()
#(<class '__main__.Son'>,
# <class '__main__.Father'>,
# <class '__main__.Mother'>,
# <class 'object'>)
print(Son.__mro__)

python内置属性__mro__可以查看方法搜索顺序

多继缺点:承容易引发冲突(重名),增加代码复杂度

多态

多态的前提是存在继承关系,存在方法重写,表现为不同子类调用父类方法会有不同的实现

class Animal(object):
    """父类: 动物类"""
    def shout(self):
        print("动物会叫")
class Dog(Animal):
    """子类: 狗"""
    def shout(self):
        print("汪汪")
class Cat(Animal):
    """子类: 猫"""
    def shout(self):
        print("喵喵")

cat = Cat()
cat.shout()
dog = Dog()
dog.shout()

# 一个接口通过多态多种实现
def test(animal):
    animal.shout()

test(cat)
test(dog)

类的实例化

# __new__ object基类提供的内置静态方法
# 1.在内存中为对象分配空间 2.返回对象的引用
class Test:
    def __init__(self):
        print("this is init")

    def __new__(cls, *args, **kwargs):
        print("this is new")
        return super().__new__(cls)



test = Test()

Python单例模式

class A:

    singleton = None

    def __init__(self):
        pass

    def __new__(cls, *args, **kwargs):
        if cls.singleton is None:
            cls.singleton = object.__new__(cls)
        return cls.singleton


a = A()
b = A()
print(a)
print(b) # a和b内存地址相同

特殊方法

__call__

def func():
    print("this is func")

#callable判断是否为可调用对象
print(callable(func)) #True

class MyClass:
    def __call__(self, *args, **kwargs):
        print("这是可调用对象")


myClass = MyClass()
myClass() #可以调用
print(callable(myClass)) #True

文件基础操作

#open() 创建一个file对象,默认是以只读模式打开
#read(n) n表示从文件中读取的数据长度,没有值默认读取文件所有内容
#write() 将指定内容写入文件
#close() 关闭文件

#文件名.name 返回打开的文件名
#文件名.mode 返回文件访问模式
#文件名.closed 检查文件是否关闭

f = open("test.txt")
print(f.name) #文件名
print(f.mode) #文件访问模式
f.close()

读取文件内容

read()

f = open("test.txt")
context = f.read()
print(context)
f.close()

readline()

f = open("test.txt")
while True:
    line = f.readline()
    if not line:
        break
    print(line)

readlines()

f = open("test.txt")
lines = f.readlines()
for line in lines:
    print(line)

文件模式

模式 可做操作 若文件不存在 是否覆盖
r 只读 报错 -
r+ 可读可写 报错
w 只写 创建
w+ 可读可写 创建
a 只写 创建 否,追加写
a+ 可读可写 创建 否,追加写

可读可写会影响效率,开发中一般以只读只写形式操作文件

w,w+模式默认会先清空原内容,即使没有做任何操作

其它模式

rb模式:以字节(二进制)方式读取文件中的数据
wb模式: 以字节(二进制)方式往文件中写入数据
ab模式: 以字节(二进制)方式往文件末尾追加写入数据

文件内容定位

#tell() 显示文件内当前位置,即文件指针当前位置
#seek(offset,whence) 移动文件读取指针到指定位置 offset:偏移量 whence:起始位置
f = open("test.txt","w+")
f.write("hello word")
pos = f.tell() #当前文件指针所在位置
print("当前位置: ",pos)
f.seek(0,0)
pos = f.tell()
print("当前位置: ",pos)
print(f.read())
f.close()

Python中的with语句

在Python中,with语句是一种上下文管理器,用于简化资源管理,如文件操作。它封装了try…except…finally的模式,使得代码更加清晰和易读。当你使用with语句时,Python会在进入代码块之前调用上下文管理器的*enter方法,并在退出时调用exit*方法,即使遇到错误也是如此。

with open("test.txt","r+") as f:
    f.write("hello word")

自动执行 f.close()

Encoding参数

Python中file对象具有encoding参数,encoding参数的默认值与平台有关,比如Windows默认字符编码为GBK

encoding表示编码集,根据文件的实际保存编码进行获取数据,对于我们而言,使用更多的是utf-8

with open("test.txt","r+",encoding='utf-8') as f:
    f.write("你好,世界!")

使用utf-8写入中文字符

目录常用操作

  1. 文件重命名:os.rename(旧文件名,新文件名)
  2. 删除文件:os.remove(目标文件名)
  3. 创建文件夹:os.mkdir(文件夹名)
  4. 获取当前目录:os.getcwd()
  5. 获取目录列表:os.listdir(目录)
  6. 删除文件夹:os.rmdir(文件夹名)

使用前需要导入os模块

迭代器

可迭代对象可以通过for循环遍历内部元素,在python中可迭代对象需要实现__iter__()方法

for循环的实现:

  1. 先通过__iter__()获取到可迭代对象的迭代器
  2. 对获取到的迭代器不断调用__next__()方法来获取下一个值并赋值给临时变量
li = [1,2,3,4]
it = iter(li)
#遍历迭代器
while True:
    try:
        item = next(it)
        print(item)
    except StopIteration:
        break

#等同于
for e in it:
    print(e)

生成器

生成器(generator)是一种特殊的迭代器,它允许函数在保持状态的情况下产生一系列的值,使得函数可以在每次调用时返回下一个值。这种机制使得生成器非常适合处理大型数据集或复杂的计算,因为它们不需要一次性将所有数据加载到内存中。

#在函数中使用了yield关键字就是生成器函数
def gen():
    print("第一次")
    yield 1
    print("第二次")
    yield 2
    print("第三次")
    yield 3

#遇到yeild关键字函数就会挂起返回,下一次执行从挂起点继续执行
gen_01 = gen()
next(gen_01)
next(gen_01)
next(gen_01)

生成器使用

def gen(n):
    for i in range(n):
        yield i

gen = gen(10)
for e in gen:
    print(e)

可迭代对象:指的是实现了python迭代协议的对象,可以通过for..in..实现遍历,如list,str,dict...

迭代器:可以记住自己遍历位置的对象,直观体现就是可以使用next()函数返回值

生成器:本质上是一个特殊的迭代器,python中提供的一种快速实现迭代器的一种手段

可迭代对象 > 迭代器 > 生成器

多线程

进程是操作系统进行资源分配的基本单位

线程是cpu调度的基本单位,每个进程至少有一个线程

导入线程模块 import threading

import threading
import time

def worker1():
    for i in range(5):
        print("work-1 is working")
        time.sleep(2)

def worker2():
    for i in range(5):
        print("work-2 is working")
        time.sleep(1)

if __name__ == '__main__':
    #创建子线程
    t1 = threading.Thread(target=worker1)
    t2 = threading.Thread(target=worker2)

    # 打印线程名
    print(t1.name, t2.name)

    #启动线程1,2
    t1.start()
    t2.start()

    #主线程等待t1,t2执行结束后继续执行
    t1.join()
    t2.join()

    print("main thread is over")

传递参数执行线程

import threading
import time

def worker1(name):
    for i in range(5):
        print(f"{name} is working")
        time.sleep(2)

def worker2(name):
    for i in range(5):
        print(f"{name} is working")
        time.sleep(1)

if __name__ == '__main__':
    #创建子线程,同时传递参数
    t1 = threading.Thread(target=worker1,args=('thread-1',))
    t2 = threading.Thread(target=worker2,args=('thread-2',))

    #启动线程1,2
    t1.start()
    t2.start()

    #主线程等待t1,t2执行结束后继续执行
    t1.join()
    t2.join()

    print("main thread is over")

==元组只有一个参数时末尾一定要加逗号,如 args=('thread-1',),否则会传递字符串导致报错==

多线程数据共享问题

import threading
a = 0
b = 1000000
def add1():
    global a
    for i in range(b):
        a += 1
    print("第一次累加的结果是:",a)


def add2():
    global a
    for i in range(b):
        a += 1
    print("第二次累加的结果是:",a)

if __name__ == "__main__":
    a1 = threading.Thread(target=add1)
    a2 = threading.Thread(target=add2)
    a1.start()
    a2.start()

会发现第二次累加结果并不是2000000,因为存在资源竞争问题,第二次累加使用了旧的资源值,因此总的累加值总是少于预期

线程同步

使用互斥锁方式

概念:对共享数据进行锁定,保证同一时刻只有一个线程操作

方法:

  • acquire(): 上锁

  • release():释放锁

注意:这两个方法必须成对出现,否则容易造成死锁

import threading
from threading import Lock
a = 0
b = 1000000
lock = Lock()
def add1():
    global a
    lock.acquire()
    for i in range(b):
        a += 1
    print("第一次累加的结果是:",a)
    lock.release()

def add2():
    global a
    with lock:
        for i in range(b):
            a += 1
        print("第二次累加的结果是:", a)

if __name__ == "__main__":
    a1 = threading.Thread(target=add1)
    a2 = threading.Thread(target=add2)
    a1.start()
    a2.start()

可以使用with lock写法保证代码块执行完毕后自动释放锁

进程

进程是操作系统进行资源分配和调度的基本单位,是操作系统结构的基础

一个正在运行的程序或软件就是一个进程

multiprocessing模块提供了Process类代表进程对象

参数

  • target:执行的目标任务,即子进程需要执行的任务

  • args:以元组形式传参

  • kwargs:以字典形式传参

from multiprocessing import Process
import os
def func1():
    print("this is func1")

def func2():
    print("this is func2")

if __name__ == '__main__':
    p1 = Process(target=func1)
    p2 = Process(target=func2)
    p1.start()
    print(f"process name is{p1.name}, pid is {p1.pid}")
    p2.start()
    print(f"process name is{p2.name}, pid is {p2.pid}")
    print(f"main process pid is {os.getpid()}, father process pid is {os.getppid()}")

进程之间不共享全局变量

进程之间通信

Queue队列,这里使用multiprocessing模块下的Queue

方法介绍

  • q.put():放入数据
  • q.get():取出数据
  • q.empty():判断队列是否为空
  • q.qsize():返回当前队列包含的消息数量
  • q.full():判断队列是否满了
from multiprocessing import Process,Queue

queue = Queue()

def writeData(q):
    print("write Data")
    for i in range(4):
        q.put(i)

def readData(q):
    print("read Data")
    for i in range(q.qsize()):
        print(q.get())

if __name__ == '__main__':
    p1 = Process(target=writeData,args=(queue,))
    p2 = Process(target=readData,args=(queue,))
    p1.start()
    p1.join()
    p2.start()

协程

协程是python中另外一种实现多任务的方式,只不过比线程更小,占用更小执行单元。它自带CPU上下文,这样只要在合适的时机,我们就可以把一个协程切换到另一个协程。只要这个过程中保存或恢复CPU上下文那么程序还是可以继续运行的。

生成器(Generators)是Python中实现协程的一种方式,它通过内置的yield关键字来暂停和恢复执行。当函数遇到yield
时,会暂停执行并返回一个值,下次调用时会从上次暂停的地方继续执行。yield
实际上是一个特殊的return语句,它会保存当前的状态(包括局部变量和执行上下文),当再次调用时,这些状态会被恢复。

greenlet

第三方封装好的库来实现协程

pip install greenlet

from greenlet import greenlet

def func1():
    print('func1 start')
    g2.switch()
    print('func1 writing data...')
    print('func1 end')

def func2():
    print('func2 start')
    g1.switch()
    print('func2 writing data...')
    print('func2 end')

if __name__ == '__main__':
    g1 = greenlet(func1)
    g2 = greenlet(func2)

    g1.switch()
    g2.switch()

执行结果:

func1 start
func2 start
func1 writing data…
func1 end
func2 writing data…
func2 end

gevent

pip install gevent

在gevent中遇到IO操作会自动切换协程,属于主动式切换

import gevent
def func1():
    print('func1 start')
    print('func1 writing data...')
    gevent.sleep(1) #遇到耗时操作
    print('func1 end')

def func2():
    print('func2 start')
    print('func2 writing data...')
    gevent.sleep(1) #遇到耗时操作
    print('func2 end')

if __name__ == '__main__':
    g1 = gevent.spawn(func1)
    g2 = gevent.spawn(func2)

    g1.join()
    g2.join()
    
    #joinall的作用是等待所有协程任务结束后再退出
    # gevent.joinall([
    #     gevent.spawn(func1),
    #     gevent.spawn(func2)
    # ])

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以邮件至 1300452403@qq.com

文章标题:Python快速上手

字数:9.8k

本文作者:Os467

发布时间:2024-12-02, 17:37:33

最后更新:2024-12-02, 17:37:25

原始链接:https://os467.github.io/2024/12/02/python/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

×

喜欢就点赞,疼爱就打赏