python 原理

时间:2023-03-10 05:04:57
python 原理

语言类型:

  • 分类:编译型、解释型,强类型、弱类型

    • 编译型:编译完在执行

      • 代表语言:C,C++
      • 优点:运行速度快
      • 缺点:开发速度慢
    • 解释型:逐行编译

      • 优点:开发速度快

      • 缺点:运行速度慢

      • 代表语言:Python,JavaS,PHP

编码:

  • 8bit = 1byte 1024B(byte) = 1KB 1024KB = 1M
  • ASCII: 英文编码 英文一个字节
  • GBK : 国标码 英文 一个字节,中文两个两个
  • Unicode: 万国码 英文两个 个字节, 中文四个字节
  • UTF-8 : 万国码升级 英文一个字节, 欧洲两个字节,中文三个字节

python2和python3区别:

  • 代码: python2 代码混乱,重复 python 源码规范,清晰,简单优美

  • 编码: python2 编码为ascii python3 内存编码为Unicode

  • print: python2 中print不需要添加括号 python3 中需要添加括号了

  • input: python2 中输入是raw-input python3 中是input

  • nonload: python2 中没有nonload python3 中新加入的

  • 类: python2 经典类,深入优先 python3 不存在经典类,都是新式类,mro算法也叫c3

基础数据类型:

  • 字符串:str

    • upper,lower,startwith,endswith,replace,strip,split,join,count,isdigit,index/find(不报错)
  • 列表:list

    • append,insert,extend,pop,remove,del,改:li[] = "海洋" 查:根据索引,切片查询

      count,index,sort,reverse

  • 列表的注意事项:

    • 循环一个列表时,删除元素时,剩下的值会向前近一步 (索引也会更改),
    • 不要改变列表的大小,这样会影响你最终结果
  • 列表去重:

    • 方法一:
  • 创建新的列表,for循环列表中每个元素是否不在新列表中,不在则追加

    • 方法二:

    • l1 = [33,22,33,44,44,22,55,11]
      l2 = list(set(l1)) #set去重转成列表
      l2.sort(key = l1.index) #使用列表进行排序,保证原顺序
      print(l2)
  • 列表删除:

    • 列表删除只可从后往前删除,

    • a = [1,2,3,4,5,6,7,8]
      
      for i in range(len(a)-1,-1,-1):
      if a[i] > 5:
      a.pop(i)
      else:
      pass
      print(a)

  • 字典:dict

    • 增加:dic['name'] = ['hai'] 有则改之,无则增加 ,dic.setdefault("name","hai") 有则不变,无则增加

    • 删除:dic.pop('key',)

    • 修改:dic['name'] = 'junli'

    • 查看:print(dic['name'],dic.get('name') #get没有返回none,好用!!!

    • fromkeys:

      • 创建空字典:dic = dict.fromkeys("可迭代对象")
    • enumerate:

      • 枚举,for i in enumerate(字典): 每次循环索引位置和key
    • update:

      • dic.update('name','18') 添加
      • dic.update(name = '太白金星') 更改
      • dic.update([(1, 'a'),(2, 'b')]) 列表中的元祖添加到字典
      • dic1.update(dic2) 更新:字典融合,有则覆盖,无则增加
  • 元祖:tuple

    • 只读列表,不可修改

    • tu1= (1) 如果元祖中只有一个元素,那么他还是本身元素

    • count 统计元祖中字符出现的次数

    • index 通过元素查找索引

  • 集合:set

    • set集合里存放的是可迭代类型
    • 列表去重
    • 交集,并集,差集(两个数据进行的操作)

文件操作:

  • 文件操作的格式:

  • with open('文件的读',encoding='utf-8') as f1:

    • print(f1.read())
    • with: 使用with打开文件,不用关闭文件句柄
  • open: 是内置内置函数,调用操作系统的接口

    • encoding:设置文件读写编码格式,默认是操作系统编码

  • mode操作方法:

  • 读取:r(默认是r) 追加:a ,写入:w 非文本文件不能指定编码:rb

    • 先读后写(可以追加):r+

    • 追加读:a+

  • 文件句柄操作方法:

    • read readline() write() read(n)按光标读 readlines()将内容聚合列表
  • 替换文件:

  • # 另一种打开文件的方式: 帮助自动关闭文件;可以同时开多个文件,用逗号隔开;也可以同时操作一个文件
    with open("a", mode="r", encoding="utf-8") as f,\
    open("b", mode="a", encoding="utf-8") as f1: import os
    print(os.getcwd()) # 查看当前工作路径
    os.remove() # 删除文件
    os.rename() # 更改文件名.(新名,旧名)

Python 中的空间和作用域:

  • 三个空间:

    • 内置名称空间:当你运行python程序,会开启一个内置名称空间,将内置函数print等等加载到空间中

    • 全局名称空间:加载当前py文件中的变量,和函数外声明的变量,加载到全局空间中,变量与值对应关系

    • 局部名称空间:函数执行时,会开辟出局部空间(也叫临时名称空间)

  • globals() 查看全局存储的全局变量

  • locals() 查看当前存储的变量

  • 全局作用域---->内置名称空间和全局名称空间

  • 局部作用域---->局部名称空间

  • 作用:

    • 作用域对与变量而言,声明变量在程序里可使用的范围

      局部作用域可以引用全局作用域变量,局部作用域不能改变全局变量

  • 加载顺序:
    • 内置--->全局--->局部 (从外到内)
  • 取值顺序:
    • 就近原则LEGB

同一代码块:

  • 优点:能够进行复用,提高一些字符串,这样在数字处理时间和空间上提升性能,节省内存
  • 适用对象:
    • 列表,字典,可变的数据类型不会复用的
    • int:任何数字在同一代码块下都会复用
    • str:几乎所有的字符串都会符合缓存机制

不同代码块:

  • 小数据池规则:
    • 小数据池,是不同代码块下的缓存机制,也叫缓存机制和驻留机制,在编程语言当中都有类似的东西
    • 主要是用于提高效率数字类型在时间和空间上的性能,避免创建重复的数据,节省内存

  • 缓存规则:
    • int:-5-256整数进行缓存,当你将整数赋值给变量时,直接引用已经创建好的缓存对象
    • str:
      • 1.字符串在做乘法(乘法不为1)的时候长度不能超过20
      • 2.自己定义的字符串长度不限制,字符串必须是字母,数字,下划线组成
      • 3.特殊字符定义1个的时候,进行驻留

运算符:

  • 算数运算符:
    • * / 加减乘除 %取余 2**4 幂 //整除返回的部分
  • 比较运算符:
    • == 等于 !+ 不等于 >= 大于等于 <= 小于等于
  • 逻辑运算符:
    • 顺序:not >>> and >>> or
    • x and y x 为真返回y x为假返回x
    • x or y x为真返回x x 为假返回y
    • not x x为真返回假 x为false返回true

深浅copy:

  • 浅copy:

    • 拷贝的数据半共享,当你浅copy一个嵌套的列表,将拷贝第一层数据会指向同一个内存地址,如果你修改数据,指向会断开,第二层数据也就是嵌套的列表他没有拷贝,元素的内存地址是共用的一个。

  • 深拷贝:

    • 拷贝的数据不共享,深拷贝他会逐层判断,将不可变的数据类型指向同一个内存地址,可变的数据类型跟原列表不共用一个,元素指向的内存地址都是一样的。

函数原理:

函数闭包:

  • 闭包存在函数嵌套中,内层函数对外层函数中非全局变量进行了引用或者修改,这个变量就是*变量,他不会随着函数的运行结束而消失,保证了数据安全

    • 当*变量被调用时,闭包形成
    • 判断函数是不是闭包,print(ret.__code__.co_freevars)
  • 作用:

    • 保证了数据安全性,*变量不会在内存中消失,而且全局还引用不到
  • 应用环境:

    • 用在与装饰器

开放封闭原则:

  • 开放原则:在源码不改变的情况下,增加一些额外的功能
  • 封闭原则:不改变源码,对与更改是封闭的。
  • 装饰器符合开放封闭原则

函数:

  • 函数是以功能为导向,一个函数封装一个功能,你写完这个函数可以随掉随用,函数他减少了代码的重复性,增强了代码的可读性

  • 高阶函数:

    • 一个函数可以作为参数传递给另一个函数,或者一个函数的返回值为另一个函数,递归也是高阶函数
    • map filter reduce 都是高阶函数
  • 匿名函数 lambda:一句话函数,用于内置函数,匿名函数结合内置函数使用

    • func = lambda x,y : x+y 名字func :前形参 :后返回值
    • 示例1:
      • func = lambda x : (x[0],x[2]) 返回多个元素加()
    • 示例2:
      • func = lambda x,y :x if x > y else y
      • func = lambda *args : max(args) #返回最大的值,也可以添加三元运算>
    • 示例3:
      • [lambda :i for i in range(10)] 这样的都是十个为9的内存地址,遇()执行

  • 内置函数:***加key的 dict,abs,sum,min,max,sorted,map,reduce,filter(68种)

    • float int list dict set tuple id len next type input print help

    • 加key的格式:

      • 将最小的值返回,dic[x] 返回值是什么,就按什么比大小***
      • dic = {'a':3,'b':2,'c':1}
      • print(dic[min(dic,key=lambda x:dic[x])]) key=lambda 函数名 :前形参 :后返回值
  • 函数名应用:

    • 函数名就是变量
    • 函数名加()执行
    • 函数名可以作为容器类型(list)的元素
    • 函数名可以作为函数的参数传递
    • 函数名可以作为函数的返回值

函数参数:

  • *的魔法作用:

  • *args 位置参数聚合成一个元

  • kwargs 关键位参数聚合成一个元

  • 实参加* 代表打散,聚合给*arg

  • 实参加** 代表打散,只能用于字典,聚合给**kwargs 打散的key不能是int类型

  • 参数说明:

    • 位置参数:从左至右,一一对应

    • 默认参数:传值覆盖,不传则默认

    • 仅限关键字参数:放在默认参数后面

  • 形参顺序:

    • 位置参数,*args,默认参数,仅限关键字参数,**kwargs
  • 实参顺序:

    • 位置参数,关键字参数,混合参数(位置参数,关键字参数)
  • 注意事项:

    • 默认参数的坑,当你的默认参数是可变类型,不会消失,使用的是同一个内存地址。
    • 函数名指向的是函数的内存地址,加上()就可执行。
  • 示例:

    • def func(a,*args,sex='男',c,**kwargs,):
      print(a)
      print(sex)
      print(args)
      print(c)
      print(kwargs)
      func(1,2,3,sex='女',name='海洋',age=18,c='666')

  • 当函数的实参位置不传值的话,可变数据类型不会消失,会使用同一个内存地址

    • def f(x,l=[]):
      for i in range(x):
      l.append(i*i)
      print(l) f(2)
      f(3,[3,2,1])
      f(3)

递归函数:

  • 递归函数就是自己调用自己

    ​ 函数或者其他代码都可以解决递归解决的问题,但是递归在某些时候能够出奇制胜

    ​ 人理解函数,神理解递归

  • 默认的递归次数:

    • 1000(次数越多,速度越慢)
    • 递归根本:逆推,要有终止条件return
  • 递归示例1:

    • #打印列表中的每个值
      l2 = [1, 3, 5, ['海洋','俊丽', 34, [33, 55, [11,33]]], [77, 88],66]
      def func(alist):
      for i in alist:
      if type(i) == list:
      func(i) # func(['海洋,'俊丽',34]) #当i为列表时一直调用本身,节省for循环
      else:
      print(i)
      func(l2)
  • 递归示例2:

    • #循环获取路径下的文件
      import os
      li = []
      def dir(path):
      path_file = os.listdir(path) #os.listdir 已列表的形式获取目录中文件夹和文件 for i in path_file:
      if os.path.isdir(os.path.join(path, i)): #判断是否为目录
      dir(os.path.join(path, i)) #将使用jion拼接使用函数再次调用给函数本身
      else:
      li.append(os.path.join(path, i)) #是文件就返回
      return li print(dir(r'路径'))
  • 递归示例3:

    • import os
      import re
      li = []
      def dir(path):
      path_file = os.listdir(path) #os.listdir 已列表的形式获取目录中文件夹和文件 for i in path_file:
      if os.path.isdir(os.path.join(path, i)): #判断是否为目录
      dir(os.path.join(path, i)) #将使用jion拼接使用函数再次调用给函数本身
      else:
      li.append(os.path.join(path, i)) #是文件就返回
      return li dir(r'E:\Pycharm-代码\作业\23期\day27-网络编程') for png in li:
      if re.findall("png",png) == ["png"]: #使用re模块findall判断文件是否已png结尾
      jpg = str(png).replace(".png",".jpg") #将png路径替换成jpg
      os.rename(png,jpg) #文件重命名

列表推导式:

简单,快捷,装逼

  • 循环模式:
    • li = [i for i in range(2,101,2)] 构建1-100的偶数
  • 筛选模式:
    • [i for i in range(1,31) if i %3 ==0 ] if筛选能被3整除的

生成器表达式:

  • (i for i in range(1,11)) 使用()扩起来就可以了, 就是生成器表达式

可迭代对象:

  • 可以重复迭代取值的一个工具,内部含有 __iter__方法的就是可迭代对象

    查看是否含有可迭代对象方法if __iter__ in dir()

  • 优点:

    • 可迭代对象是一个操作方法灵活,侧重于对少量数据灵活处理(并且内存足够)
  • 缺点:

    • 占用内存
    • 不能迭代取值(索引,字典的key)

迭代器 ltertor:

  • 可以重复迭代的工具,内部含有__iter____next__方法就是迭代器,

    iter方法是将可迭代对象转换成迭代器

    next方法是迭代器进行取值(无法找到下一个结果时会出现StopIteration异常)

  • 优点:迭代取值,节省内存(迭代之后元素消失),惰性机制不走回头路

  • 缺点:操作方法少,不直观,查询速度较慢(时间换空间)

  • 作用:

    *为什么要使用迭代器,当你要获取文件内容时,传统方式是读取文件内容到内存空间中,在使用

​ for循环遍历,如你文件较大,这样会占用极大的内存空间,而使用迭代器是可以一个迭代读取一

​ 段内容,节省内存空间,这也是迭代器存在的作用

  • 使用环境:
    • 逐行遍历文件内容,构建和扩展集合类型,元祖拆包

生成器:

  • 生成器的本质就是迭代器,函数中包含yield方法,是生成器函数。

  • 生成器产生方式: 生成器函数(yield和yield from) 生成器表达式() python内置函数 lamdba

  • 取值方式:next() for循环 list列表

    • yield:

      • 函数中包含yield方法,是生成器函数,当你去调用函数的时候,返回值是一个生成器地址,当你想执行内容需要调用next()方法
    • yield from:

      • 代替了生成器函数里的内层循环,在函数中如果yield from list 会将list可迭代对象变成迭代器返回

迭代器和生成器区别:

  • 可迭代对象和迭代器的区别:

    • 可迭代对象操作方法多,但是占用内存,不能循环迭代取值,如果你侧重于数据灵活操作并且内存足够,可以使用可迭代对象

    • 迭代器操作方法少,不灵活,但是可以迭代取值,取完值消失,查询速度慢

  • 迭代器和生成器的区别:

    • 迭代器都是内置函数转化过来的

    • 生成器是我们自己用Python代码构建的

装饰器原理:

  • 装饰器本质是一个函数:他要装饰一个函数,在不改变原函数的源码以及调用方式的前提下,给其增加额外的功能
    • 语法糖:@index = index = test_time(index)
    • 用途:
    • 登录认证,打印日志,访问记录

  • 最简单的装饰器函数传参:

    • def warpper(s):  #s = func1
      def inner(*args,**kwargs): #4-聚合接受inner的实参海洋
      print(666) #执行func1函数前操作
      ret = s(*args,**kwargs) #5给打散实参执行func1函数,
      return ret #6将func1,return执行结果传给inner
      return inner #2-返回inner给warpper @warpper #1-语法糖:func1 = warpper(func1)
      def func1(a):
      return f"{a}欢迎登录"
      print(func1("海洋")) #3-func1 = inner('海洋') 7 print结果
  • 装饰器传参:装饰器嵌套三层函数,最外层函数接受装饰器实参,可以对形参进行操作判断

    • def wrapper(n):
      def wrapper1(f):
      def inner(*args,**kwargs):
      name = input("输入用户名")
      pwd = input("输入密码")
      with open(n,encoding='utf-8') as f1: #n等于语法糖传过来的值
      for i in f1:
      j,k = i.strip().split('|')
      if name == j and pwd == k:
      ret = f(*args,**kwargs)
      return ret
      return inner
      return wrapper1 @wrapper('qq') #将qq传给最外层wrapper形参
      def qq():
      print('成功访问qq') @wrapper('weixin')
      def tiktok():
      print('成功访问微信')
      qq()
      tiktok()
  • 两个装饰器,装饰一个函数:先执行@wraper1 执行完的结果是@wraper2的形参。

    如果两个装饰器内层函数一样,调用函数时先走最上面装饰器

    • def wrapper1(func1):  # func1 = f原函数
      def inner1():
      print('wrapper1 ,before func') # 2
      func1()
      print('wrapper1 ,after func') # 4
      return inner1 def wrapper2(func2): # func2 == inner1
      def inner2():
      print('wrapper2 ,before func') # 1
      func2() # inner1
      print('wrapper2 ,after func') # 5
      return inner2 @wrapper2 # f = wrapper2(f) 里面的f == inner1 外面的f == inner2
      @wrapper1 # f = wrapper1(f) 里面的f == func1 外面的 f == inner1
      def f():
      print('in f') # 3
      f() # inner2()

模块原理:

模块:

  • 一个.py文件就是模块,节省代码,容易维护,组织结构更清晰

  • 常用的模块:

    • json,pickle:序列化,sys:路径,os :文件操作
    • hashlib :加密 time datetime:时间
    • random:随机取值

  • 模块的两种引用方式:
    • import 模块名
    • from 模块名 import 变量

  • 模块的运行方式:

    • 脚本方式:直接用解释器执行,或者pycharm中右键运行。
  • 模块方式:被其他的模块导入,为导入他的模块提供资源(变量,函数定义,)

  • 模块的分类:

    • 内置模块:标准库,python解释器自带的,time,os,sys,等等200多种
  • 第三方库(模块):各种大神写的模块,通过pip install...安装的6000多种自己写的模块,

    • 自定义模块

  • 引用模块发生三件事:

    • 1,import tbjx 将haiyang.py文件(变量,函数名)加载到内存
    • 2,在内存中创建一个以太tbjx命名的名称空间
    • 3,通过haiyang.xx名称空间的名字.等方式引用此模块的名字(变量,函数名)

序列化:

  • 将一个数据结构(list,tuple,dict)转化成一个特殊序列的过程,并且可以翻转回去,这个转化的过程就是序列化

  • Json:

    • dict,list, tuple,str,int
    • 不同语言都遵循使用的一种数据转换格式,可以互相使用json传输数据
  • Pickle:

    • 支持所有数据类型

      python语言遵循的一种数据化转换格式,只能在python中使用

  • 作用:

    • 为什么要用序列化,因为硬盘存储和网络传输时只能接受bytes类型的。
    • dumps 将数据转换 dump将数据转换并写入文件(json存储空间小,pickle存储空间大)

开发规范:

  • 规范开发:文件加载问题,代码可读性差,查询麻烦

  • PEP8规范:

  • 1. bin       start.py          程序开关:启动代码目录,run()
    2. conf settings.py 配置文件:放置一些静态参数,比如文件路径,数据库配置,软件默认设置
    3. core src.py 主逻辑函数: 主要存放就是核心逻辑功能,核心函数都在这里
    4. lib common.py 公共组件: 存放装饰器,密码加密功能,日志功能存放
    5. db register 存放数据库文件, 注册表,用户信息
    6. log access.log 存放系统的日志
  • bin start:启动start文件,工作目录已经写入到sys.path中(内存中),在这个工作目录下的文件都可以互相引用

  • import os
    import sys
    #动态获取工作目录os.path.dirname 通过file文件找到父级目录上的父级目录也就是整个代码的工作目录
    BASH_PATH = os.path.dirname(os.path.dirname(__file__))
    sys.path.append(BASH_PATH) from core import src
    if __name__ == '__main__': #防止别的文件引用执行
    src.run()
  • settings:数据库地址进行拼写

  • import os
    BASE_PATH = os.path.dirname(os.path.dirname(__file__))
    register_path = os.path.join(BASE_PATH,'db','register')
  • src:主逻辑需要引用数据库地址和装饰器文件,都需要引用

  • from  conf import settings
    from lib import common
  • common:装饰器这块引用src中的状态和登录函数,规范其实也就是文件之间的互相引用

  • from core import src