1. 关于 Python 的变量的默认值

运行以下代码,会有惊喜!

def func(x,l=[]):
    for i in range(x):
        l.append(i*i)
    print(l)

func(2)
func(3,[3,2,1])
func(3)

这段代码会输出:

[0, 1]
[3, 2, 1, 0, 1, 4]
[0, 1, 0, 1, 4]

惊不惊喜,意不意外。 这是因为

  • Python 中,参数的默认值,也相当于变量一样,指向内存中的唯一的地址,除非你传进来了一个参数,改变这个参数的指向。
  • 如果你在程序的运行过程中(使用了默认变量),修改了这个变量的值(可变),那么恭喜你,你的默认参数已经变了!

2. 一行代码实现 1--100 的和

简单点我们就用:

sum(range(1,101))

这里主要要记住的就是 range 是返回一个==迭代器==,==左闭右开==。

python2.x 的 range 是返回的数组,

python3.x 的 range 是返回迭代器。

尝试运行:

print(sum(range(1, 101)))

# 只是摆在这里告诉你这是最高效(NB)的写法。没任何毛病。不要死脑筋
print("5050")

print(range(1, 10))
print(list(range(1, 10)))

out:

5050 5050 range(1, 10) [1, 2, 3, 4, 5, 6, 7, 8, 9]

那为什么会这样呢?其实在 Python3 中 range()函数返回的对象很像一个列表,但是它确实不是一个列表,它只是在迭代的情况下返回指定索引的值,它并不会在内存中产生一个列表对象,官方解释说这样做是为了节约内存空间。通常我们称这种对象是可迭代的,或者是可迭代对象。

这里就要引入另外一个叫迭代器的概念,迭代器可以从一个可迭代对象中连续获取指定索引的值,直到索引结束。比如 list()函数,所以在上面的例子中,我们可以用 list()这个迭代器将 range()函数返回的对象变成一个列表。

由此可以看出:range()函数返回的是一个可迭代对象(类型是对象),而不是列表类型;list() 函数是对象迭代器,把对象转为一个列表,返回的变量类型为列表。

3. 如何在一个函数内部修改全局变量

在函数内部使用 global 声明,修改全局变量,作用域的知识点。

a = 5
b = 4


def fn():
    # 在需要修改全局变量的时候,在前面加上global修饰
    global a
    print("Global a: ", a)
    print("Global b: ", b)
    a = 4
    # 此路不通,IDE都会告诉你不能执行
    # 全局变量在不声明global的时候不能修改,但是能读取
    # b = 5
    print("Global a: ", a)


print("Global a: ", a)
fn()
print("Global a: ", a)

out:

Global a: 5 Global a: 5 Global b: 4 Global a: 4 Global a: 4

4. Python 的几个标准库

库名作用
os提供了不少与操作系统相关联的函数
sys通常用于命令行参数
re正则匹配
math数学运算
datetime处理日期时间

5. 字典如何删除键和合并两个字典

字典基操

dic = {"name": "zs", "age": 18}

print(dic)

del dic["name"]

print(dic)

dic2 = {"name": "ls"}

dic.update(dic2)

print(dic)
  • 补充一下知识点,删除对象属性的时候调用__delattr__()或者super.__delattr__()或者直接使用del self.__dict__[key]

6. 关于 GIL 锁

  • 一号解释:

    GIL 是 python 的全局解释器锁,同一进程中假如有多个线程运行,一个线程在运行 python 程序的时候会霸占 python 解释器(加了一把锁即 GIL),使该进程内的其他线程无法运行,等该线程运行完后其他线程才能运行。如果线程运行过程中遇到耗时操作,则解释器锁解开,使其他线程运行。所以在多线程中,线程的运行仍是有先后顺序的,并不是同时进行。

    多进程中因为每个进程都能被系统分配资源,相当于每个进程有了一个 python 解释器,所以多进程可以实现多个进程的同时运行,缺点是进程系统资源开销大。

  • 二号知识点:

    多线程和多进程是不一样的。

    多进程是真正的并行,而多线程是伪并行,实际上他只是交替执行。

    是什么导致多线程,只能交替执行呢?是一个叫 GIL(Global Interpreter Lock ,全局解释器锁)的东西。

    GIL 的概念:

    任何 Python 线程执行前,必须先获得 GIL 锁,然后,每执行 100 条字节码,解释器就自动释放 GIL 锁,让别的线程有机会执行。这个 GIL 全局锁实际上把所有线程的执行代码都给上了锁, 所以,多线程在 Python 中只能交替执行,即使 100 个线程跑在 100 核 CPU 上,也只能用到 1 个 核

    这个是 Python 的解释器 CPython 引入的概念。还有其他解释器。但是默认的认为 Python == CPython

    默许了 Python 有 GIL 锁这个东西。

    避免 GIL 锁的方法:

    • 使用多进程代替
    • 不使用 CPython

7. Python 实现列表去重

Python 内置的集合是没有重复值的,数据到集合(set)里跑一圈就行了。

记住,是跑一圈!!!最后还要转成集合(list)

lst = [11, 12, 13, 12, 15, 11, 13]

tmp = set(lst)

print(type(tmp))

lst = [i for i in tmp]

print(lst)

out:

<class 'set'> [11, 12, 13, 15]

8.阐述*args**kwargs

*args*kwargs主要用于函数定义。

  • 你可以将不定数量的参数传递给一个函数。这里的不定的意思是:预先并不知道函数使用者会传递多少个参数给你,所以在这个场景下使用这两个关键字。*args是用来发送一个非键值对的可变数量的参数==元组(不可变)==给一个函数
  • **kwargs允许你将不定长度的键值对,作为参数传递给一个函数。如果你想要在一个函数里处理带名字的参数你应该使用**kwargs。这里传输的是字典。

这里有类似前端的解包的概念。

补充:

  1. 对于参数的传递,不指定 key 值的时候,按顺序传递。
  2. args 和 kwargs 只是大家约定俗成写这个,也就不要乱起名字了。
def func(a, *args, **kwargs):
    print(type(args))
    print(args)
    print(type(kwargs))
    print(kwargs)


func(10, 20, 30, b=40, d=50)

out:

<class 'tuple'> (20, 30) <class 'dict'> {'b': 40, 'd': 50}

9.解释一波装饰器

对于一些可以将函数传递的语言,都可以用装饰器。

装饰器(decorator)接受一个 callable 对象 (可以是函数或者实现了 call 方法的类)作为参数,并返回一个 callable 对象。

灵魂就是将函数 A 传递到另外一个函数 B 中,然后使用 B 调用 A,这样可以给 A 增加额外的功能。

简单的装饰器:

def outer(my_func):
    print("step 1 : outer")
    def inner(num):
        print("step 2 : 进入内部函数")
        return my_func(num)
    return inner


@outer
def my_func(num):
    print("step 3 : my_func,传入的参数:", num)
    num += 1
    return "加1之后:" + str(num)


print(my_func(num=100))

out:

step 1 : outer step 2 : 进入内部函数 step 3 : my_func,传入的参数: 100 加 1 之后:101

高级装饰器:

def outer(str):
    print("outer")
    def outer1(func):
        print("outer1")
        def inner():
            print(str)
            print("inner")
            return func()
        return inner
    return outer1

@outer("哈哈哈")
def func():
    print("func")

func()

out:

outer outer1 # 到此处都是装饰的时候生成的,下面才是调用的时候生成的 哈哈哈 inner func

一个用来实现函数运行时间计算的装饰器:

def print_func_time(function):
    @wraps(function)
    def func_time(*args, **kwargs):
        t0 = time.perf_counter()
        result = function(*args, **kwargs)
        t1 = time.perf_counter()
        print("Total running time: %s s" % (str(t1 - t0)))
        return result
    return func_time

10. Python 常见内建数据类型

表示含义
int整形
bool布尔型
str字符串
list列表
tuple元组
dict字典