目录
1.python的深拷贝和浅拷贝
浅拷贝得到的对象与原始对象共享一块内存地址,当一个对象发生改变时,另一个也会发生改变。可以使用切片,copy.copy(),list()等完成浅拷贝。
深拷贝是指创建一个全新的对象,与原始对象互不干扰。可以使用copy.deepcopy().
2.python的多线程可以用多个cpu吗?
因为python每个进程会有一个全局解释器锁的存在(GIL),全局(包括多个cpu)只有一个线程可以占用这个锁。这使得每个python进程只能在一个cpu上运行。那么就有人问了,既然多线程起不到作用,为什么还要用那,那是因为还存在线程阻塞的问题,对于密集i/o操作,网络请求等阻塞型任务时,当一个线程被阻塞了,他可以释放GIL,让其他线程继续工作,可以适当加速,所以还是有用的。
那么我们如何解决GIL的问题?可以使用多进程,每个进程都有各自独立的GIL锁,互不影响。使用其他python解释器,如PYPY。
对于密集IO操作,一般用多线程,对于密集计算型任务,一般使用多进程。多线程共享一个内存空间,所以信息传输更加简单。
3.python垃圾回收机制
python的垃圾回收机制会自动处理对象的内存分配和释放问题。一般有两种回收机制:一种是引用计数,python里的每个对象都有一个计数器,用于记录指向该对象的引用次数,当变为0时,会自动释放。还有一种循环垃圾回收机制,循环引用是指一组对象相互引用形成闭环,且没有任何外部引用指向这个闭环,这时使用循环垃圾回收机制,周期性的检测清理,在进行垃圾回收时,首先会对当前所有对象进行标记,然后回收那些未被标记的对象。这样就会清除掉循环引用等无法通过引用计数判断的对象。
4.迭代器和生成器的区别
迭代器和生成器是python中用于处理可迭代对象的两种不同机制。迭代器学要手动实现__iter__和__next__方法,并在next中定义迭代逻辑。生成器是一种特殊的函数,每次调用生成器时,他会保存当前的执行状态,并在yield语句处暂停,下次再次调用时,会从上次结束的位置继续执行,直到函数结束或遇到下一个yield。生成器简化了迭代器的创建过程,适用于惰性计算。而迭代器适用于复杂的迭代逻辑。生成器可以节省内存,因为它在运行时只保留当前的状态,而不是一次性生成所有值。
当生成器函数被调用时,它并不立即执行,而是返回一个生成器对象。每当生成器的 __next__()
方法被调用时,生成器函数才会从上次暂停的地方继续执行,直到遇到下一个 yield
语句
def fibonacci(): //生成器函数
a, b = 0, 1
while True:
yield a
a, b = b, a + b
# 使用生成器逐个打印斐波那契数列的前十个数
fib = fibonacci()
for _ in range(10):
print(next(fib))
class MyIterator:
def __init__(self, data):
self.data = data
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index >= len(self.data):
raise StopIteration
value = self.data[self.index]
self.index += 1
return value
# 创建一个列表
my_list = [1, 2, 3, 4, 5]
# 创建一个迭代器对象
my_iter = MyIterator(my_list)
# 使用迭代器遍历列表并打印每个元素
for item in my_iter:
print(item)
my_iter是一个迭代器对象,使用for循环对其进行遍历,每次得到的值,都为next中处理后返回的。
5.装饰器
装饰器是一种特殊的函数,用来扩展,包装和修改函数,而无需改变其原始定义。通常采用函数嵌套的形式,接受一个函数作为参数。通过闭包来实现装饰器的定义。
def counter(func):
count = 0
def wrapper(*args, **kwargs):
nonlocal count
count += 1
print(f"函数 {func.__name__} 已被调用 {count} 次")
return func(*args, **kwargs)
return wrapper
# 使用装饰器修饰目标函数
@counter
def say_hello(name):
print(f"Hello, {name}!")
# 调用被修饰的函数
say_hello("Alice")
say_hello("Bob")
6.python的数据类型
字符串(string)
列表类型(list)用[]中括号表示。
元组类型(tuple):用小括号表示。
字典类型(dict):{},键值之间用冒号,不同键值对之间用逗号。
集合(set):{}。
7.python列表删除元素的三种方法
del通过索引删除指定位置的元素,或者全部删除。
remove通过值来删除列表中第一个匹配项。
pop通过索引来删除元素,并返回值。
my_list = [1, 2, 3, 4, 5]
del my_list[2] # 删除索引为2的元素
print(my_list) # 输出:[1, 2, 4, 5]
del my_list # 删除整个列表
# 注意:使用 del 关键字删除的元素是永久性删除,不可恢复。
my_list = [1, 2, 3, 4, 5]
my_list.remove(3) # 删除值为3的元素
print(my_list) # 输出:[1, 2, 4, 5]
# 如果要删除多个匹配项,需要循环调用 remove 方法。
my_list = [1, 2, 3, 4, 5]
value = my_list.pop(2) # 删除索引为2的元素,并返回该元素的值
print(my_list) # 输出:[1, 2, 4, 5]
print(value) # 输出:3
# 如果不指定索引,pop 方法默认删除最后一个元素,并返回其值。
8.python yield 和 return 的区别
return会结束函数的执行,而yield会返回一个值,并暂停函数的执行,然后记住函数的执行状态,以便从暂停处继续执行。
9.python里面的可哈希对象和不可哈希对象
可哈希对象可以作为字典的键或集合的元素,其包含:数值类型,字符串,元组。不可哈希对象包含:列表,字典,集合
10.如何对字典根据值进行排序
字典的排序需要用到items()方法,根据值来操作,则需要lambda函数
my_dict = {'apple': 5, 'banana': 2, 'orange': 8, 'kiwi': 3}
my_dict = dict(sorted(my_dict.items(), key = lambda x : x[1]))
11.python的lambda函数
lambda函数是一种匿名函数,可以在需要一个简单的函数时使用。后面跟着参数列表和冒号,冒号后面是函数表达式,这个表达式的结果作为lambda的返回值。
# 使用 lambda 函数计算两个数的和
sum_func = lambda a, b: a + b
result = sum_func(3, 4)
print(result)
12.python中的魔法方法
__new__方法:是一个对象实例化之前被调用的方法,他负责创建并返回一个实例对象。参数列表中至少有一个必须是类本身
__init__方法:是一个对象实例化之后调用的方法,它用于对实例对象进行属性的初始化设置。参数列表中至少有一个必须是实例对象,通常被命名为self。
__call__方法:使得一个对象可以像函数一样被调用。当将对象作为函数调用时,会触发__call__
方法。
13.python在内存上有哪些优化
引用计数和循环垃圾回收机制。迭代器和生成器按需生成数据,而不是一次性生成所有结果。
14.python中的类方法和静态方法的区别
在 Python 中,类方法(class method)和静态方法(static method)都是类中定义的特殊方法,但它们在功能和使用方式上有一些区别。
- 类方法(class method):
- 使用
@classmethod
装饰器来修饰方法,使之成为类方法。 - 第一个参数被约定为
cls
,指向类本身,可以通过该参数访问类的属性和调用类的其他方法。 - 类方法可以被类本身或类的实例调用。
- 类方法通常用于执行与类相关的操作,如创建对象、修改类属性等。
- 使用
- 静态方法(static method):
- 使用
@staticmethod
装饰器来修饰方法,使之成为静态方法。 - 静态方法没有对类或实例进行特殊处理,因此没有默认的第一个参数指向类或实例。
- 静态方法可以通过类或实例访问。
- 静态方法通常用于执行与类相关的功能,但不需要访问类的属性或调用类的其他方法
- 使用
- 类方法和静态方法均为类的特殊方法,通过装饰器修饰成特定类型的方法。
- 类方法通过第一个参数
cls
可以访问类的属性和调用类的其他方法,主要用于执行与类相关的操作。 - 静态方法没有默认的类或实例参数,可以通过类或实例访问,主要用于执行与类相关的功能,但不需要访问类的属性或调用类的其他方法。
class MyClass: @classmethod def class_method(cls): print("This is a class method") # 调用类方法 MyClass.class_method() class MyClass: @staticmethod def static_method(): print("This is a static method") # 调用静态方法 MyClass.static_method()
15.python的多线程和多进程的实现
import threading
# 定义一个函数作为线程的执行体
def my_thread():
print("Thread started.")
# 创建 10 个线程
threads = []
for i in range(10):
thread = threading.Thread(target=my_thread)
thread.start()
threads.append(thread)
# 等待所有线程执行完毕
for thread in threads:
thread.join()
print("All threads are done.")
在上面的代码中,我们创建了一个线程列表 threads
,然后通过循环创建了 10 个线程,并将它们添加到列表中。每个线程都通过调用 start()
方法启动并开始执行。接着,我们使用另一个循环遍历线程列表,并调用每个线程的 join()
方法,程序会在该方法处阻塞,直到该线程执行完毕才继续执行下一行代码。这样可以确保主线程在所有子线程完成后再继续执行。
import multiprocessing
# 定义一个函数作为进程的执行体
def my_process():
print("Process started.")
# 创建 10 个进程
processes = []
for i in range(10):
process = multiprocessing.Process(target=my_process)
process.start()
processes.append(process)
# 等待所有进程执行完毕
for process in processes:
process.join()
print("All processes are done.")
16. python的传参与传值
主要根据参数是可变对象还是不可变对象来说。对于不可变对象,如数值类型,字符串,元组,是值传递,不会影响原始参数。
对于可变对象,如列表,字典,集合等,是引用传递,会直接在原始对象上操作。
17.猴子补丁
是指在运行时,动态修改已有的代码或者类成员。
class MyClass:
def method(self):
print("Original method")
# 定义新方法
def new_method(self):
print("Patched method")
# 将新方法绑定到原始类
MyClass.method = new_method
# 创建对象并调用方法
obj = MyClass()
obj.method() # 输出: "Patched method"
18.python中is与 ==的区别
is是判断两个对象是否引用了内存中的同一块地址。 == 是判断两者的值是否相等。
19,python常用简单写法
列表生成式 [i for i in range(n)]
字典生成式(元组变为字典) {x[0] : x[1] for x in turple}
列表切片反转 [::-1]
20.python2和python3里面的range区别
在python2中range()函数会返回一个列表类型,python3会返回一个迭代器。仅在需要的时候才会计算下一个元素,占用内存更少,可以用list(range(n))直接使用。
21.项目里面经常看到__init__.py文件
它可以使当前目录被视为一个包,并提供了进行初始化和控制导入行为的能力,他是构建和组织复杂项目的重要组成部分。__init__.py
文件还可以通过定义 __all__
变量来控制包的导入行为。__all__
是一个列表,指定了在使用 from package import *
语句导入包时,应该导入哪些子模块或对象