python_essay

python_essay

文章大部分参考:https://www.nowcoder.com/discuss/426117497918660608

什么是Python?

Python是一种编程语言,

赋值、深拷贝、浅拷贝

赋值:对象的赋值就是简单的引用。 赋值操作不会开辟新的空间,只是复制了对象的引用

如:

python
a = [1, 2, 3]
b = a

此时a和b完全相同,修改a即相当于对b进行修改

拷贝本质上是在说“新变量”和“原变量”是不是共享同一块内存数据

浅拷贝

浅拷贝有三种形式:切片操作、工厂函数、copy模块中的copy函数

只复制第一层对象,里面嵌套的子对象依然共享。也就是说只复制第一层,更深层的数据依然共享。

例:

python
import copy

a = [[1,2],[3,4]]
b = copy.copy(a)

此时a is b,将得到False

外层列表已经复制了,但ab不是同一个对象 但判断a[0] is b[0]将得到True 这说明:内层列表还是同一个对象,并没有真正复制

深拷贝

深拷贝,是指重新分配一块内存,创建一个新的对象,并且将原对象中的元素,以递归的方式,通过创建新的子对象拷贝到新对象中。因此,新对象和原对象没有任何关联

python
import copy

a = [[1,2],[3,4]]
b = copy.deepcopy(a)

现在a is bFalse,且a[0] is b[0]也是False 说明:内层列表也复制了,完全独立

解释型语言和编译型语言

编译型语言

通过专门的编译器,把所有源代码一次转换为特定平台的可执行文件,然后再运行。一次编译,多次运行。如C、C++

优点:一次编译,多次运行;脱离编译环境,并且运行效率高。 缺点:依靠编译器跨平台性差、可移植性差。

  • 编译时根据对应的运行环境生成机器码,不同的操作系统之间移植就会有问题,需要根据运行的操作系统环境编译不同的可执行文件。
  • 编译之后如果需要修改就需要整个模块重新编译

解释型语言

由专门的解释器,根据需要将部分源代码临时转换成特定平台的机器码,然后执行。边解释边执行。比如Python, PHP.

优点:

  • 跨平台性好,通过不同的解释器,将相同的源代码解释成不同平台下的机器码。 缺点:
  • 一边执行一边转换,效率不高

混合型语言

如Java, C#。兼具执行效率和跨平台性,不直接编译成机器码,需要先编译成中间码(java是编译成字节码),需要中间语言运行库类似java虚拟机(JVM),边解释边运行

is和== 的区别

==比较操作符,只是判断对象的(value)是否一致,而is判断的是对象之间的身份(内存地址)是否一致。对象的身份可以通过id()方法来查看

os和sys的区别

os模块是Python标准库中提供的与操作系统交互的模块,提供了访问操作系统底层的接口,里面有很多操作系统的函数。

sys模块负责程序与Python解释器的交互。

可变对象和不可变对象

在Python中,一切皆有对象,对象必有的三个属性:地址、类型、值

可变对象

  • 当对象的值发生变化,但内存地址没有改变时,则说明是可变类型
  • python里的可变对象有:列表字典集合
  • 引用可变对象时,会创建新的内存地址,当可变对象值发生改变时,原内存地址不会改变
  • 引用传递主函数向调用函数传递的参数是可变类型时,实际上是将实参的引用传入了调用函数,对引用的操作等于其指定的对象进行操作

不可变对象

  • 当对象的值发生变化,但内存地址也发生改变时,则说明是不可变类型
  • python里的不可变对象有:元组字符串数值
  • python在引用不可变对象时,会寻找该对象是否被创建过,若该对象已创建,则变量会直接引用该对象,不会再申请新的内存空间。
  • 值传递主函数向调用函数传递的参数是不可变类型时,实际上只是将实参的拷贝(即临时副本)传递给了被调用函数,并不是实参本身,这样被调函数不能直接修改主调函数中变量的值

注意:如果直接将list2 = list1,那么list1和list2的地址会是相同的。只是换了不同的名称而已。

* arg和** kwarg作用

允许在调用函数的时候传入多个实参

*arg会把位置参数转化为tuple,**kwarg会把关键字参数转化为dict

with如何使用

with所求值的对象必须有一个enter()方法,一个exit()方法

with后面的语句被求值后,返回对象的__enter__()方法被调用,这个方法的返回值将被赋值给as后面的变量。当with后面的代码块全部被执行完之后,将调用前面返回对象的exit()方法

__new____init__

__init__初始化方法,__new__方法是真正的构造函数__new__实例创建时被调用,它的任务是创建并返回该实例,是静态方法__init__是实例创建之后调用的,然后设置对象属性的一些初始值

__new__方法在__init__方法之前被调用,并且__new__方法的返回值将传递给__init__方法作为第一个参数,最后__init__给这个实例设置一些参数

装饰器

Python的装饰器本质上是一个嵌套函数,它接受被装饰的函数(func)作为参数,并返回一个包装过的函数

本质上是一个“接受函数并返回新函数“的函数

作用:不修改原函数代码的情况下,给函数额外加功能

Python装饰器广泛应用于缓存权限校验(如django中的@login_required和@permission_required装饰器)、性能测试(比如统计一段程序的运行时间)和插入日志等应用场景

比如有个函数:

python
def func():
    print("我是原函数")

现在想:

  • 调用前打印“开始”
  • 调用后打印“结束” 但:
  • 不想改 func 内部代码 于是:
python
def decorator(fn):
    def wrapper():
        print("开始")
        fn()
        print("结束")
    return wrapper

使用:

python
func = decorator(func)
func()

@语法糖: 为了写着方便:

python
@decorator
def func():
    print("hello")

等价于:

python
def func():
    print("hello")

func = decorator(func)

迭代器和生成器

可迭代对象

python中一个非常强大的功能,它可以访问容器(字符串、列表、元祖、集合、字典、range)。 迭代是通过for循环遍历对象中的每一个元素,将元素取出来的过程。所以:容器都是可迭代对象

可迭代对象除了包含常见的序列,还包括迭代器

迭代器

  • 迭代器是一个可以记住遍历的位置的对象
  • 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退
  • 迭代器有两个基本方法:iter()next()
  • 字符串,列表或元组对象都可用于创建迭代器
python
list=[1,2,3,4]
it = iter(list)
print(next(it))

生成器

  • 在python中,使用了yield的函数被称为生成器
  • 跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作。
  • 在调用生成器运行的过程中,每次遇到yield时函数会暂停并保存当前所有的运行信息,返回yield的值,并在下一次执行next()方法时从当前位置继续运行
  • 调用生成器函数时,函数体并不执行,只返回一个生成器对象
  • 在对生成器对象使用next方法或者遍历的时候,生成器函数体才真正执行
python
def gen():
    print("开始")
    yield 1

    print("继续")
    yield 2

    print("结束")
    yield 3

执行:

python
g = gen()

print(next(g))
print(next(g))
print(next(g))

输出:

python
开始
1

继续
2

结束
3

yield:

txt
return + 记住返回位置

普通return: 返回后函数直接结束 yield:

  • 返回值
  • 函数没有结束
  • 下次还能从原位置继续

filter map reduce

filter函数用于过滤序列,接收一个函数和一个序列,把函数作用于序列的每个元素上,然后根据返回值是True还是False决定保留还是丢弃该元素

python
mylist = list(range(10))
list(filter(lambda x: x % 2 == 1, mylist))
[1, 3, 5, 7, 9]

map函数传入一个函数和一个序列,并把函数作用到序列的每个元素上,返回一个可迭代对象。

python
list(map(lambda x: x % 2, mylist))
[1, 0, 1, 0, 1, 0, 1, 0, 1]

reduce函数用于递归计算,同样需要传入一个函数和一个序列,并把函数和序列元素的计算结果与下一个元素进行计算

python
from functools import reduce
reduce(lambda x, y: x + y, range(101))
5050

全局变量 局部变量 闭包

在函数定义的变量为全局变量。全局变量可以在函数中直接进行访问,但是在修改全局变量时,为了避免与局部变量产生混淆,需要先加上global声明,然后再修改。

闭包是Python编程一个非常重要的概念,如果一个外函数中定义一个内函数,且内函数体内引用到了体外的变量,这时外函数通过return返回内函数的引用时,会把定义时涉及到的外部引用变量和内函数打包成一个整体(闭包) 返回。

闭包=函数+它记住的外部变量

例:

python
def outer():
    x = 10
    def inner():
        print(x)
    return inner

执行:

python
f = outer()

注意: 这里outer已经执行结束了 正常来说:

python
x = 10

这个变量应该没了。 但:

text
f()

还能输出10 这就是闭包

python
lst =[lambda x: x*i for i in range(4)]
res = [m(2) for m in lst]
print(res)

预期的结果为:[0,2,4,6] 实际输出为:[6,6,6,6]

这个为什么叫闭包?

因为

python
lambda x:x*i

这个lambda用到了自己函数体外的变量i

即内函数:lambda,外部作用域:for循环所在作用域

匿名函数

匿名函数的关键字为lambda,表现形式为lambda 参数:返回值,lambda后面的参数就是函数的形参,冒号后面的表达式就是返回值。

lambda表达式的意义:

  • 对于只有一行的函数,使用此方式可以省去定义函数的过程,使代码简洁明了;
  • 对于不需要重复使用的函数,此方式可以在用完之后,立即释放,提高程序执行性能

垃圾回收机制

引用计数(主要手段)+标记清除(辅助)+分代回收(辅助)

1、引用计数

在Python中,使用了引用计数这一技术来实现内存管理,一个对象被创建完成后就有一个变量指向这个对象,那么就这个对象的引用计数为1,以后如果有其他变量指向这个对象,其引用计数也会相应增加,如果一个变量不再执行这个对象,那么这个对象的引用计数减1。如果一个对象没有任何变量指向这个对象,也即引用计数为0,那么这个对象就会被python回收。

优点:

  • 高效。
  • 运行期没有停顿。可以类比一下Ruby的垃圾回收机制,也就是实时性:一旦没有引用,内存就直接释放了。不用像其他机制等到特定时机。实时性还带来一个好处:处理回收内存的时间分摊到了平时。
  • 对象有确定的生命周期。
  • 易于实现。 缺点:
  • 维护引用计数消耗资源,维护引用计数的次数和引用赋值成正比。
  • 无法解决循环引用的问题。比如现在有两个对象分别为aba指向了bb又指向了a,那么他们两的引用计数永远都不会为0。也即永远得不到回收。 2、标记清除

针对循环引用的情况,python引入标记清除算法。

标记清除算法是一种基于追踪回收技术实现的垃圾回收算法。它分为两个阶段

  • 第一阶段是标记阶段,GC会把所有的活动对象打上标记
  • 第二阶段是把那些没有标记的对象非活动对象进行回收。 3、分代回收

分代回收是建立在标记清除技术基础之上的。是一种以时间换空间的操作方式。

Python将内存根据对象的存活时间划分为不同的集合,每个集合称为一“代”, Python将内存分为了3“代”,分别为年轻代(第0代)、中年代(第1代)、老年代(第2代),他们对应的是3个链表,它们的垃圾收集频率与对象的存活时间的增大而减小。新创建的对象都会分配在年轻代,年轻代链表的总数达到上限时,Python垃圾回收机制就会被触发,把那些可以被回收的对象回收掉,而那些不会回收的对象就会被移到中年代去,依此类推,老年代中的对象是存活时间最久的对象,甚至是存活于整个系统的生命周期内。

协程

协程本质上是:

一个可以主动暂停、以后再继续执行的函数

普通函数:

  • 一路执行到底
  • 不能中途挂起 协程:
  • 执行到一半可以暂停
  • 之后还能从原位置恢复

协程的核心作用:

提高IO密集型程序的执行效率

常见的IO操作:

  • 网络请求
  • 数据库查询

协程的思想:

txt
等待的时候,让CPU去执行别的任务

如何定义协程? Python使用:

python
async def

定义协程函数

例:

python
async def test():
    print("hello")

调用async函数不会立即执行,而是返回一个协程对象

await的作用

python
await 某个耗时操作

表示:

当前协程先暂停,去执行别的协程

asyncio

asyncio是Python的协程调度框架 作用:

  • 管理协程
  • 调度协程
  • 控制暂停与恢复

asyncio.run()

作用:

python
asyncio.run(main())

用于:

  • 启动事件循环
  • 执行协程

协程通常是单线程的任务切换,不是多个线程同时运行

新故事即将发生
反向SSH内网服务器远程连接

评论区

评论加载中...