Python基础---面试题汇总

article/2025/9/11 14:00:34

前言

本文只涉及Python相关的面试题,面向中高级Python开发,太基本的题目不收录。

更希望通过代码演示,原理探究等来深入讲解某一知识点,做到融会贯通。

另外部分演示代码有兴趣的可以找我拿。

语言基础篇

Python的基本数据类型

Python3 中有六个标准的数据类型:

  • Number(数字)(包括整型、浮点型、复数、布尔型等)
  • String(字符串)
  • List(列表)
  • Tuple(元组)
  • Set(集合)
  • Dictionary(字典)

Python3 的六个标准数据类型中:

  • 不可变数据(3 个):Number(数字)、String(字符串)、Tuple(元组);
  • 可变数据(3 个):List(列表)、Dictionary(字典)、Set(集合)。

Python是静态还是动态类型?是强类型还是弱类型?

  • 动态强类型语言(不少人误以为是弱类型)
  • 动态还是静态指的是编译器还是运行期确定类型
  • 强类型指的是不会发生隐式类型转换

js就是典型的弱类型语言,例如在console下面模拟一下数字和字符串相加,会发现发生了类型转换。

而Python会报TypeError

什么是鸭子类型

“当一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”

鸭子类型关注的是对象的行为,而不是类型。比如file,StringIO,socket对象都支持read/write方法,再比如定义了__iter__魔术方法的对象可以用for迭代。

下面用一个例子来模拟鸭子的类型:

pythonclass Duck:
def say(self):
print("嘎嘎")
class Dog:
def say(self):
print("汪汪")
def speak(duck):
duck.say()
duck = Duck()
dog = Dog()
speak(duck) # 嘎嘎
speak(dog) # 汪汪

什么是自省

自省是运行时判断一个对象类型的能力。

python一切皆对象,用type, id, isinstance获取对象类型信息。

自省,也可以说是反射,自省在计算机编程中通常指这种能力:检查某些事物以确定它是什么、它知道什么以及它能做什么。

与其相关的主要方法:

  • hasattr(object, name)检查对象是否具体 name 属性。返回 bool.
  • getattr(object, name, default)获取对象的name属性。
  • setattr(object, name, default)给对象设置name属性
  • delattr(object, name)给对象删除name属性
  • dir([object])获取对象大部分的属性
  • isinstance(name, object)检查name是不是object对象
  • type(object)查看对象的类型
  • callable(object)判断对象是否是可调用对象

python3和python2的对比

  • print成为函数
  • 编码问题。python3不再有unicode对象,默认str就是unicode
  • 除法变化。python3除号返回浮点数,如果要返回整数,应使用//
  • 类型注解。帮助IDE实现类型检查
  • 优化的super()方便直接调用父类函数。Python3.x 和 Python2.x 的一个区别是: Python 3 可以使用直接使用 super().xxx 代替 super(Class, self).xxx :
  • 高级解包操作。a, b, *rest = range(10)
  • keyword only arguments。限定关键字参数
  • chained exceptions。python3重新抛出异常不会丢失栈信息
  • 一切返回迭代器。range, zip, map, dict.values, etc. are all iterators
  • 性能优化等。。。

python如何传递参数

python官方文档上的话:

“Remember that arguments are passed by assignment in Python. Since assignment just creates references to objects, there’s no alias between an argument name in the caller and callee, and so no call-by-reference per Se.”

准确地说,Python 的参数传递是赋值传递 (pass by assignment),或者叫作对象的引用传递(pass by object reference)。Python 里所有的数据类型都是对象,所以参数传递时,只是让新变量与原变量指向相同的对象而已,并不存在值传递或是引用传递一说。

根据对象的引用来传递,根据对象是可变对象还是不可变对象,得到两种不同的结果。如果是可变对象,则直接修改。如果是不可变对象,则生产新对象,让形参指向新对象

可以具体结合下面的代码实例来模拟:

pythondef flist(l):
l.append(0)
print(id(l)) # 每次打印的id相同
print(l)
ll = []
print(id(ll))
flist(ll) # [0]
flist(ll) # [0,0]
print("=" * 10)
def fstr(s):
print(id(s)) # 和入参ss的id相同
s += "a"
print(id(s)) # 和入参ss的id不同,每次打印结果不相同
print(s)
ss = "sun"
print(id(ss))
fstr(ss) # a
fstr(ss) # a

python的可变/不可变对象

不可变对象: bool/int/float/tuple/str/frozenset 可变对象:list/set/dict

这里继续看两个代码例子,看下输出的是什么

pythondef clear_list(l):
l = []
ll = [1,2,3]
clear_list(ll)
print(ll)
def fl(l=[1]):
l.append(1)
print(l)
fl()
fl()

答案是

[1,2,3]
[1]
[1,1]

对于第一题,l = []这一步,创建了一个新的对象,并将l贴上去(注意函数里面的l和外面的l是形参和实参的区别,不要以为是同一个),所以原来的 l 并没有改变

对于第二题,默认参数只计算一次。

有兴趣的小伙伴可以再试一下这个例子:

pythona = 1
def fun(a):
print("func_in",id(a))
a = 2
print("re-point",id(a), id(2))
print("func_out",id(a), id(1))
fun(a)

答案是:

func_out 2602672810288 2602672810288
func_in 2602672810288
re-point 2602672810320 2602672810320

关于Python的参数传递,可变/不可变对象,再推荐一个stackoverflow上面的回答。

Arguments are passed by assignment. The rationale behind this is twofold:
the parameter passed in is actually a reference to an object (but the reference is passed by value)some data types are mutable, but others aren't
So: If you pass a mutable object into a method, the method gets a reference to that same object and you can mutate it to your heart's delight, but if you rebind the reference in the method, the outer scope will know nothing about it, and after you're done, the outer reference will still point at the original object.
If you pass an immutable object to a method, you still can't rebind the outer reference, and you can't even mutate the object.

Python中的 *args 和 **kwargs

用来处理可变参数,*args被打包成tuple,**kwargs被打包成dict

我们看一些代码例子:

pythondef print_multiple_args(*args):
print(type(args), args)
for idx, val in enumerate(args): # enumerate()枚举函数
print(idx, val)
print_multiple_args('a', 'b', 'c')
# 通过将列表前加*打包成关键字参数,指明了接收值参数必须是*args
print_multiple_args(*['a', 'b', 'c'])
def print_kwargs(**kwargs):
print(type(kwargs), kwargs)
for k, v in kwargs.items():
print('{}: {}'.format(k, v))
print_kwargs(a=1, b=2)
# 给字典前加**打包成关键字参数,指明接收值的参数必须是**kwargs
print_kwargs(**dict(a=1, b=2))
def print_all(a, *args, **kwargs):
print(a)
if args:
print(args)
if kwargs:
print(kwargs)
print_all('hello', 'world', name='monki')

输出为:

<class 'tuple'> ('a', 'b', 'c')
0 a
1 b
2 c
<class 'tuple'> ('a', 'b', 'c')
0 a
1 b
2 c
<class 'dict'> {'a': 1, 'b': 2}
a: 1
b: 2
<class 'dict'> {'a': 1, 'b': 2}
a: 1
b: 2
hello
('world',)
{'name': 'monki'}

python异常机制

可参考Python官方文档上的异常层级分类

docs.python.org/zh-cn/3/lib…

python异常代码块示例:

pythontry:
# func # 可能会抛出异常的代码
except (Exception1, Exception2) as e: # 可以捕获多个异常并处理
# 异常处理的代码
else:
# pass # 异常没有发生的时候代码逻辑
finally:
pass # 无论异常有没有发生都会执行的代码,一般处理资源的关闭和释放

什么是Python中的GIL?

全局解释器锁 GIL,英文名称为 Global Interpreter Lock,它是解释器中一种线程同步的方式。

对于每一个解释器进程都具有一个 GIL ,它的直接作用是限制单个解释器进程中多线程的并行执行,使得即使在多核处理器上对于单个解释器进程来说,在同一时刻运行的线程仅限一个。 对于 Python 来讲,GIL 并不是它语言本身的特性,而是 CPython 解释器的实现特性。

Python 代码被编译后的字节码会在解释器中执行,在执行过程中,存在于 CPython 解释器中的 GIL 会致使在同一时刻只有一个线程可以执行字节码。 GIL 的存在引起的最直接的问题便是:在一个解释器进程中通过多线程的方式无法利用多核处理器来实现真正的并行。

因此,Python的多线程是伪多线程,无法利用多核资源,同一个时刻只有一个线程在真正的运行。

GIL的限制了程序的多核执行

  • 同一个时间只能有一个线程执行字节码
  • CPU密集程序难以利用多核优势
  • IO期间会释放GIL,对IO密集程序影响不大

面对GIL的存在,我们有可以有多个方法帮助我们提升性能

  • 在 IO 密集型任务下,我们可以使用多线程或者协程来完成。
  • 可以选择更换 Jython 等没有 GIL 的解释器,但并不推荐更换解释器,因为会错过众多 C 语言模块中的有用特性。
  • CPU密集可以使用多进程+进程池。
  • 将计算密集型任务转移到 Python 的 C / C++ 扩展模块中完成。

为什么有了GIL还要关注线程安全?

GIL 保证的是每一条字节码在执行过程中的独占性,即每一条字节码的执行都是原子性的。GIL 具有释放机制,所以 GIL 并不会保证字节码在执行过程中线程不会进行切换,即在多个字节码之间,线程具有切换的可能性。

我们可以用python的dis模块去查看a += 1执行的字节码,发现需要有多个字节码去完成,线程具有切换的可能性,所以它是非线程安全的。

一个操作如果是一个字节码指令可以完成就是原子的,非原子操作不是线程安全的,原子的是可以保证线程安全的。

GIL 和线程互斥锁的粒度是不同的,GIL 是 Python 解释器级别的互斥,保证的是解释器级别共享资源的一致性,而线程互斥锁则是代码级(或用户级)的互斥,保证的是 Python 程序级别共享数据的一致性,所以我们仍需要线程互斥锁及其他线程同步方式来保证数据一致。

具体关于Python的GIL的介绍,可参考我的另一篇文章《详解Python中的GIL》

什么是迭代器和生成器?

这张图比较精彩,把各种概念都总结了。

容器(container)

container 可以理解为把多个元素组织在一起的数据结构,container 中的元素可以逐个地迭代获取,可以用 in, not in 关键字判断元素是否包含在容器中。比如Python中常见的container对象有list,deque,set

可迭代对象(iterables)

大部分的 container 都是可迭代对象,比如 list or set 都是可迭代对象,可以说只要是可以返回一个迭代器的都可以称作可迭代对象。

迭代器(iterator)

python中的容器有许多,比如列表、元组、字典、集合等,对于容器,可以很直观地想象成多个元素在一起的单元,所有的容器都是可迭代的(iterable)。

我们通常使用for in 语句对可迭代的对象进行枚举,其底层机制在于:

而可迭代对象,通过 iter() 函数返回一个迭代器(iterator),迭代器提供了一个 next 的方法。调用用这个方法后,你要么得到这个容器的下一个对象,要么得到一个StopIteration 的错误。

举个例子:

python>>> x = [1, 2, 3]
>>> # Get the iterator
>>> y = iter(items) # Invokes items.__iter__()
>>> # Run the iterator
>>> next(y) # Invokes it.__next__()
1
>>> next(y)
2
>>> next(y)
3
>>> type(x)
<class 'list'>
>>> type(y)
<class 'list_iterator'>
>>> next(y)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>

上面的例子中,x=[1,2,3]是可迭代对象,这里也叫容器。y=iter(x)则是迭代器,且实现了__iter__和__next__方法。

它们之间的关系如下图所示:

可见通过 iter 方法后就是迭代器。它是一个带状态的对象,调用 next 方法的时候返回容器中的下一个值,可以说任何实现了iter和 next 方法的对象都是迭代器,iter返回迭代器自身,next 返回容器中的下一个值,如果容器中没有更多元素了,则抛异常。

迭代器就像一个懒加载的工厂,等到有人需要的时候才给它生成值返回,没调用的时候就处于休眠状态等待下一次调用。

生成器(generator)

生成器(generator)可以简单理解为懒人版本的迭代器。

它相比于迭代器的优势是,生成器并不会像迭代器一样占用大量内存。比如声明一个迭代器:[i for i in range(100000000)]就可以声明一个包含一亿个元素的列表,每个元素在生成后都会保存到内存中。但实际上我们也许并不需要保存那么多东西,只希望在你用 next() 函数的时候,才会生成下一个变量,因此生成器应运而生,在python中的写法为(i for i in range(100000000))

此外,生成器还可以有别的形式,比如生成器函数,通过yield关键字,把结果返回到next()方法中,举个例子:

pythondef frange(start, stop, increment):
x = start
while x < stop:
yield x
x += increment
for n in frange(0, 2, 0.5):
print(n)
0
0.5
1.0
1.5

相比于迭代器,生成器具有以下优点:

  1. 减少内存
  2. 延迟计算
  3. 有效提高代码可读性

我曾经对生成器和迭代器有过总结:《Python中的迭代器和生成器》

stackoverflow上有个关于yield的高赞回答:

stackoverflow.com/questions/2…

什么是协程?

内容较多,具体可以看我的这篇文章。《详解Python协程》

什么是闭包?

在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包。

简单的说,如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。来看几个简单的例子:

最简单的例子,实现加法

pythondef addx(x):
def adder(y):
return x + y
return adder
c = addx(8)
print(type(c))
print(c.__name__)
print(c(10))
复制代码
<class 'function'>
adder
18

利用闭包实现斐波那契数列

pythonfrom functools import wraps
def cache(func):
store = {}
@wraps(func)
def _(n):
if n in store:
return store[n]
else:
res = func(n)
store[n] = res
return res
return _
@cache
def f(n):
if n <= 1:
return 1
return f(n-1) + f(n-2)
print(f(10))

结语

这篇贴子到这里就结束了,最后,希望看这篇帖子的朋友能够有所收获。欢迎留言,或是关注我的专栏和我交流。


http://chatgpt.dhexx.cn/article/1reVxkGr.shtml

相关文章

python面试题汇总(史上最全)

python面试题 ✅作者简介&#xff1a;大家好我是编程ID &#x1f4c3;个人主页&#xff1a;编程ID的csdn博客 系列专栏&#xff1a;python &#x1f4ac;推荐一款模拟面试、刷题神器&#x1f449;点击跳转进入网站 对于机器学习算法工程师而言&#xff0c;Python是不可或缺的语…

Python面试题40问

**1&#xff09;什么是Python&#xff1f;**使用Python有什么好处&#xff1f; Python是一种编程语言&#xff0c;包含对象&#xff0c;模块&#xff0c;线程&#xff0c;异常和自动内存管理。Python的好处在于它简单易用&#xff0c;可移植&#xff0c;可扩展&#xff0c;内置…

Python面试常见的30个问题及答案

Python面试问题 问题1。 Python的主要功能是什么? python特性如下: 解释型动态类型面向对象简洁明了免费自由有一个庞大的社区Q.2。区分深层和浅层拷贝。 深拷贝将对象复制到另一个对象中。这意味着如果您对对象的副本进行更改,则不会影响原始对象。在Python中,我们使用…

Python编程面试题及答案(20例)

以下是一些常见的Python编程面试题以及它们的答案&#xff1a; 1.解释Python中的 GIL&#xff08;全局解释器锁&#xff09;是什么&#xff0c;它对多线程编程有什么影响&#xff1f; 答案&#xff1a;GIL是Python解释器中的一个机制&#xff0c;它确保在任何给定时间只有一个…

Python110道常见面试题

干货 | 110 道 Python 面试笔试题超强汇总&#xff01; 这几天好多留言问有没有Python面试题&#xff0c;今天统一给大家分享一遍&#xff0c;希望能帮助此时仍在找工作的同学&#xff0c;尽快找到工作&#xff0c;希望对基本知识不熟悉的同学&#xff0c;能认真做一遍&#x…

Python常考基础面试题

文章目录 Python基础面试题1、 Python 数据结构有哪些2、Python 中列表和元组的区别是什么&#xff1f;元组是不是真的不可变&#xff1f;3、什么是生成器和迭代器&#xff1f;它们之间有什么区别&#xff1f;迭代器生成器 4、什么是闭包&#xff1f;装饰器又是什么&#xff1f…

Java单例破坏以及防止

单例实现的方式有很多种&#xff0c;如有需要&#xff0c;请查设计模式——单例_程序员Forlan的博客-CSDN博客 下面我们以静态内部类为例 public class Demo{private Demo(){}public static Demo getInstance(){return Inner.demo;}private static class Inner{private static…

Java单例模式读取MySql配置文件

单例的数据库连接类&#xff1a; import com.alibaba.druid.pool.DruidDataSourceFactory; import javax.sql.DataSource; import java.io.FileInputStream; import java.sql.Connection; import java.util.Properties;public class Singleton {// 创建单例对象private static…

java单例模式--Java单例模式以及6种实现方式

文章目录 前言一、单例模式的定义二、单例模式特点三、实现单例模式的八种方式及优缺点总结 前言 今天老师考到了单例模式&#xff0c;我对这个知识点掌握的不是很好&#xff0c;所以总结此文来使自己深入了解Java中的单例模式~~同时也借鉴给和我一样不懂的友友们~~~ 一、单例…

java单例模式使用_Java单例模式的应用

单例模式用于保证在程序的运行期间某个类有且仅有一个实例。其优势在于尽可能解决系统资源。通过修改构造方法的访问权限就可以实现单例模式。 代码如下&#xff1a; public class Emperor { private static Emperor emperor null;// 声明一个Emperor类的引用 private Emperor…

创建型:Java单例模式

一.单例模式的定义&#xff1a; 单例模式确保某个类只有一个实例&#xff0c;而且自行实例化并向整个系统提供这个实例。在计算机系统中&#xff0c;线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台…

java 单例模式 参数_Java单例模式

再孬再好&#xff0c;就你一个 单例模式 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式&#xff0c;它提供了一种创建对象的最佳方式。 这种模式涉及到一个单一的类&#xff0c;该类负责创建自己的对象&#xff0c;同时确保只…

java单例接口_JAVA单例模式

一.问题引入 偶然想想到的如果把Java的构造方法弄成private,那里面的成员属性是不是只有通过static来访问呢;如果构造方法是private的话,那么有什么好处呢;如果构造方法是private的话,会不更好的封装该内呢?我主要是应用在使用普通类模拟枚举类型里,后来发现这就是传说中…

java单例模式——详解JAVA单例模式及8种实现方式

本文转载自&#xff1a;java单例模式——详解JAVA单例模式及8种实现方式_EatingSoilLang的博客-CSDN博客_java单例模式 ## 单例模式是最简单也是最基础的设计模式之一&#xff0c;下边一起学习一下单例模式&#xff01; 一.单例模式的定义&#xff1a; 单例模式确保某个类只有…

JAVA设计模式之单例模式

本文继续介绍23种设计模式系列之单例模式。 概念&#xff1a;   java中单例模式是一种常见的设计模式&#xff0c;单例模式的写法有好几种&#xff0c;这里主要介绍三种&#xff1a;懒汉式单例、饿汉式单例、登记式单例。   单例模式有以下特点&#xff1a;   1、单例类…

Java多线程(七):单例模式详解

目录 1. 什么是单例模式 2. 单例模式的特点 3. 单例模式的实现 3.1 单例模式实现步骤 3.2 单例模式实现方式 3.2.1 饿汉方式 3.2.2 懒汉方式 1. 什么是单例模式 java中单例模式是一种常见的设计模式&#xff0c;单例模式确保某个类只有一个实例&#xff0c;而且自行实例…

ARFoundation之路-3D物体识别之一

版权声明&#xff1a;Davidwang原创文章&#xff0c;严禁用于任何商业途径&#xff0c;授权后方可转载。 3D物体跟踪技术&#xff0c;是指通过图像处理技术对摄像头中拍摄到的3D物体识别定位并对其姿态进行跟踪的技术。3D物体跟踪技术的基础也是图像识别&#xff0c;但比前述图…

ARFoundation系列讲解 - 57 3D物体识别一

一、介绍 目前在ARFoundation中,3D物体识别只支持A9处理器或更高版本的iOS设备,系统版本要求iOS 12或更高版本,Android不支持此功能 。使用3D物体识别跟踪功能需要预先采集3D物体特征点信息。苹果公司给我们提供了一个采集3D物体特征点工具,我们需要下载并使用Xcode编译成应…

实战三:手把手教你实现物体识别

实战三&#xff1a;手把手教你实现物体识别 一、基于HaadAdaboost实现人脸识别 1.原理介绍&#xff08;参考下面的博客文章&#xff09; http://www.cnblogs.com/ello/archive/2012/04/28/2475419.html Haar分类器 Haar-like特征积分图方法AdaBoost级联 Haar分类器算法的…

手把手教你使用LabVIEW OpenCV dnn实现物体识别(Object Detection)含源码

文章目录 前言一、物体识别算法原理概述1、物体识别的概念2、Yolo算法原理概述 二、opencv调用darknet物体识别模型&#xff08;yolov3/yolov4&#xff09;1、darknet模型的获取2、python调用darknet模型实现物体识别3、LabVIEW调用darknet模型实现物体识别yolo_example.vi4、L…