title: python面试题
date: 2021-04-07 20:12:03
tags:
top: 6
持续更新中…
python的特点
python是一种解释性语言 python是交互式语言 python是面向对象的语言 python易于学习,易于阅读 python可跨平台运行
2.Python优缺点
优点:
易于学习、易于维护、易于阅读 一个广泛的标准库 支持交互,在终端敲命令的形式得到运行的结果 可移植性 可扩展性【可以调用C或C++写的代码】 数据库【python提供所有商业数据接口】 GUI编程(图形化界面) 可嵌入式
缺点:
运行速度相比C非常慢 代码不能加密
3.python应用场景
web开发【通过mod_wsgi模块,Apache可以运行用Python编写的Web程序。Python定义了WSGI标准应用接口来协调Http服务器与基于Python的Web程序之间的通信。一些Web框架,如Django,TurboGears,web2py,Zope等,可以让程序员轻松地开发和管理复杂的Web程序】
操作系统管理、服务器运维的自动化脚本【在很多操作系统里,Python是标准的系统组件。 大多数Linux发行版以及NetBSD、OpenBSD和Mac OS X都集成了Python,可以在终端下直接运行Python。Python编写的系统管理脚本在可读性、性能、代码重用度、扩展性几方面都优于普通的shell脚本】
网络爬虫【Python有大量的HTTP请求处理库和HTML解析库,并且有成熟高效的爬虫框架Scrapy和分布式解决方案scrapy-redis,在爬虫的应用方面非常广泛】
科学计算(数据分析)【NumPy、SciPy、Pandas、Matplotlib可以让Python程序员编写科学计算程序】
桌面软件【PyQt、PySide、wxPython、PyGTK是Python快速开发桌面应用程序的利器】
服务器软件【Python对于各种网络协议的支持很完善,因此经常被用于编写服务器软件、网络爬虫。第三方库Twisted支持异步网络编程和多数标准的网络协议(包含客户端和服务器),并且提供了多种工具,被广泛用于编写高性能的服务器软件】
游戏【很多游戏使用C++编写图形显示等高性能模块,而使用Python或者Lua编写游戏的逻辑、服务器。相较于Python,Lua的功能更简单、体积更小;而Python则支持更多的特性和数据类型】
4.交换两个变量的值
a.设置新变量当中转空间 a = 10 b = 20 temp = 0 temp = a a = b b = temp b.加减法 a = 10 b = 20 a = a + b # a = 10 + 20 b = a - b # b = 30 - 20 a = a - b # a = 30 - 10 c.python专有的交换方式 a = 10 b = 20 a, b = b, a d.异或,计算机二进制运算按位异或 一个数异或一个数两次,这个数不变 a = 10 b = 20 a = a ^ b # a = a ^ b b = a ^ b ^ b # b = a a = a ^ b # a = a(a ^ b) ^ a
5.Python中的内存管理机制
定义变量的机制:定义一个变量相当于在计算机内存中开辟了一块空间,用于存储指定的数据。
变量的本质:变量实际上存储的是数据的地址。
计算机的内存:
栈:引用【变量名】 堆:实际的数据【对象】
6.进制问题
print(int("0b1102", base=16))
分析:进制问题,注意2进制只能由1和0组成,8进制的范围是07,16进制的范围是09 + a~f
虽然是0b开头,但是中间出现了2,那么这个数是能是16进制。
7.python2.x和python3.x的版本的区别
print 函数
Python3.x当中打印方式为print()函数 print("HelloWorld") Python2.x当中打印方式为prin语句 print "HelloWorld" Python2.6和Python2.7为二者的过渡版本,上面两种方法都支持。
Unicode 编码表
Python 2.x 有 ASCII str() 类型,unicode() 是单独的,不是 byte 类型。
Python3.x有了Unicode(utf-8)字符串,以及一个字节类:byte和bytearrays。
中国 = 123
Python3.x支持上面这个写法,Python2.x不支持
除法
” / “
Python2.x当中,整型数字之间相除,产生的结果会舍去小数部分,结果也为整型。
Python3.x当中,整型数字之间相除,产生的是一个浮点数。
” // “整除
Python2.x和Python3.x运算规则一样。
异常
捕获异常的语法由 except exc, var 改为 except exc as var
使用语法except (exc1, exc2) as var可以同时捕获多种类别的异常
在2.x时代,所有类型的对象都是可以被直接抛出的,在3.x时代,只有继承自BaseException的对象才可以被抛出。
2.x raise语句使用逗号将抛出对象类型和参数分开,3.x取消了这种奇葩的写法,直接调用构造函数抛出对象即可。
在2.x时代,异常在代码中除了表示程序错误,还经常做一些普通控制结构应该做的事情,在3.x中可以看出,设计者让异常变的更加专一,只有在错误发生的情况才能去用异常捕获语句来处理。
xrange
在 Python 2 中 xrange() 创建迭代对象的用法是非常流行的。比如: for 循环或者是列表/集合/字典推导式。
这个表现十分像生成器(比如。“惰性求值”)。但是这个 xrange-iterable 是无穷的,意味着你可以无限遍历。
由于它的惰性求值,如果你不得仅仅不遍历它一次,xrange() 函数 比 range() 更快(比如 for 循环)。尽管如此,对比迭代一次,不建议你重复迭代多次,因为生成器每次都从头开始。
在 Python 3 中,range() 是像 xrange() 那样实现以至于一个专门的 xrange() 函数都不再存在(在 Python 3 中 xrange() 会抛出命名异常)。
八进制字面量表示
八进制数必须写成0o777,原来的形式0777不能用了;二进制必须写成0b111。
新增了一个bin()函数用于将一个整数转换成二进制字串。 Python 2.6已经支持这两种语法。
在Python 3.x中,表示八进制字面量的方式只有一种,就是0o1000。
不等运算符
Python 2.x中不等于有两种写法 != 和 <>
Python 3.x中去掉了<>, 只有!=一种写法
去掉repr表达式"
Python 2.x 中反引号``相当于repr函数的作用
Python 3.x 中去掉了``这种写法,只允许使用repr函数
数据类型
Python3.x中去掉了Python2.x当中的long数据类型,新增了bytes类型
str 对象和 bytes 对象可以使用 .encode() (str -> bytes) 或 .decode() (bytes -> str)方法相互转化
8.简述if语句中的单分支、双分支和多分支的区别
单分支:
判断条件是否成立,成立执行if所属下面的语句,不成立不执行。
双分支:
if—else语句:条件成立执行if所属下面的语句,不成立执行else所属下面的语句。
多分枝:
if-elif…else语句,实现多选一,从上往下依次判断,只要有一个条件成立,那只执行对应的分支语句,执行完毕,整个if语句结束,所有条件不成立,执行else当中的语句。
9.break和continue的区别
break:
作用:跳出整个循环体【结束循环,执行循环后面的语句】 注意:break讲究就近原则,跳出距离最近的循环
continue:
作用:跳过当前正在执行的循环,然后进行下一轮循环【结束本次循环,执行下一次循环】
10.is 和 == 的区别
is 是比较两个变量的【对象】的地址
== 是比较两个变量【对象】的内容
如果两个变量的地址相同,则这两个变量的内容一定相同
如果两个变量的内容相同,则这两个变量的地址不一定相同
11.短路原则
1.A and B,如果A为False,不需要计算B的值,整个表达式结果为False
2.A or B,如果A为True,不需要计算B的值,整个个表达式结果为True
3.and 和 or 混合使用
a.表达式从左往右运算,如果or的左侧为True,则会短路or后所有的表达式【不管后面连接了and还是or】
b.表达式从左往右运算,如果and的左侧为False,则短路后面所有的and,直到or出现,接着计算。
c.如果or的左侧为False,或者and的左侧为True,则不能使用短路逻辑判断
注意:被短路的表达式都不会执行
12.身份运算符
身份运算符用于比较两个对象的存储单元【地址】
is:判断两个标识符是不是引用自一个对象 is not:判断两个标识符是不是引用自不同对象
13.什么是列表切片?请举例说明
通过指定的区间和指定的步长,获取指定列表中的指定元素,生成一个新的列表。
切片之后的结果要么非空,要么为空,不会报错
list1 = [1, 2, 3, 4, 5] list2 = list1[1:3:] list2 = [2, 3] print(list1[100:]) # [] print(list1[0:-1]) # 等价于[0:4] [1,2,3,4] print(list1[5:100]) # [] print(list1[::1]) # 顺序 print(list1[::-1]) # 倒叙 print(list1[-1:0])) # 相当于list1[-1:-5:1] [] print(list1[-1:0:-1]) # 相当于list1[-1:-5:-1] [5,4,3,2]
14.列表操作中,append和extend的区别和联系
相同点:
1.都是向列表添加元素,并且都是添加在末尾。 2.二者一次只能传递一个数据 3.二者都是在原列表的基础上添加元素的,所以返回值都为None
不同点:
1.append可以向列表添加任何类型的元素,extend只能向列表添加可迭代对象。 2.append添加序列的时候会把整个序列当作一个元素添加到列表里面,extend会把序列里面的元素一个个依次添加到列表类面。
15.代码阅读
a = [1, 2, 3] b = [4, 5] c = [a, b] d = c e = c.copy() a.append(20) print(c,d,e) # 最开始c = [[1, 2, 3], [4,5]] d = [[1, 2, 3], [4,5]] # e = = [[1, 2, 3], [4,5]] # d属于直接引用 # e属于浅拷贝 # a.append(20) ————》 a = [1, 2, 3, 20] 在a的末尾添加元素20 # 由于都不是深拷贝,所以内层元素发生改变,其他列表也会随着发生改变 c = [[1, 2, 3, 20], [4,5]] d = [[1, 2, 3, 20], [4,5]] e = [[1, 2, 3, 20], [4,5]]
16.代码阅读
a = [1, 2, ['a', 'b']] b = a c = copy.deepcopy(a) a[-1].append(3) print(b) print(c)
解析:
# b是直接引用a # c是深拷贝a # 所以,a内部元素变化会对b 造成影响,但不会对c造成影响 b = [1, 2, ['a', 'b', 3]] c = [1, 2, ['a', 'b']]
17.代码阅读
a = [1, 2, ['a', 'b']] b = a c = copy.deepcopy(a) a.append(3) print(b) print(c)
解析:
""" c是深拷贝a b是直接引用 a.append(3)改变了a 的外层元素, 对深拷贝没有影响,但对于直接引用有直接的影响 """ b = [1, 2, ['a', 'b',], 3] c = [1, 2, ['a', 'b']]
18.引用赋值、浅拷贝、深拷贝
引用赋值:不管是几维列表,一个列表的元素改变,另一个随着改变
列表.copy()和copy.copy():
对于一维列表,一个列表的元素改变,另一个不会改变 对于二维列表,一个列表的元素改变另一个会随着改变
对copy.deepcopy():不管是几维列表,一个列表的元素改变,另一个都不会改变。
19.可变数据类型和不可变数据类型的区别
不可变数据类型:int, float, bool, str, tuple,通过变量赋值的方式操作,一个变量的值发生改变,另一个变量不受影响。
可变数据类型:list, dict, set,通过变量赋值的操作,一个变量内部的元素改变,另一个变量会随着改变。
20.元组和列表的区别和联系
相同点:
1.二者都是有序的 2.二者都可以存储不同类型的元素 3.二者都可以存储重复元素 4.二者的遍历方式完全相同
不同点:
1.表示方式:元组::(),列表:[] 2.可变性:元组是不可变,列表是可变的 3.使用场景:在多线程的使用场景中,为了避免多线程访问同一个数据造成的混乱,则可以使用元组。如果涉及到元素的增加或者删除,则可以使用列表。 4.元组在创建时间和占用空间都优于列表。
21.创建字典的五种方式
方式一:创建并传入键值对
dict1 = {"aa": 10, "bb": 20} print(dict1)
方式二:先创建,再传入键值对
dict2 = {} dict2["aa"] = 10 dict2["bb"] = 20 print(diict2)
方式三:dict3 = dict(key = value, …)
dict3 = dict(name = "xiaokeai", age = 10) print(dict3)
方式四:dict([(key1,value1), (key2, value2)])
dict4 = ([("a",11), ("b",22),("c",33)]) print(dict4)
方式五:dict(zip[所有的key], [所有的value])
dict5 = {zip[11,22,33],["a","b","c"]} print(dict5)
22.字典的update()方法
update():更新,合并字典,将指定字典中的键值对添加到原字典中
23.区分字典zip方法和dict.fromkeys创建字典的区别
dict1 = dict(zip([11,22],["a","b"])) print(dict1) # {11:"a", 22:"b"} dict2 = dict.fromkeys([11,22],["a", "b"]) print(dict2) # {11:["a", "b"], 22:["a", "b"]}
24.add 和uptate之间的区别和联系
相同点:
1.都是向集合中添加元素 2.都只能识别一个元素
不同点:
1.add只能添加不可变的数据类型 2.update只能添加可迭代对象【list,str,tuple,dict】 3.如果添加的数据为元组,add会将整个元组当作元素添加到集合当中,update只会将元组里面的元素添加到集合当中。
25.集合间的运算
1.交集 &
set1 = {1,2,3} set2 = {3,4,5} # 算术运算符 set3 = ste1 & set2 # 系统功能 set4 = set1.intersection(set2)
2.并集 |
set1 = {1,2,3} set2 = {3,4,5} # 算术运算符 set3 = ste1 | set2 # 系统功能 set4 = set1.union(set2)
3.差集 -
set1 = {1,2,3} set2 = {3,4,5} # 算术运算符 set3 = ste1 - set2 # 系统功能 set4 = set1.differance(set2)
4.差集的并集/并集 - 交集
set1 = {1,2,3} set2 = {3,4,5} # 算术运算符 set3 = ste1 ^ set2
26.阅读下面代码,输出结果
def test(num1,num2,*num3,num4): print(num1,num2,num3,num4) test(6,4,5,65,566,88,89,88)
分析:结果会报错,不定长参数出现在形参列表的中间,在传递实参的时候,在可变长参数后面的参数不是关键字参数,会导致可变长参数接收num2以后的所有数据,结果导致num4接收不到参数而报错。
注意:在形参列表中,不定长参数最好出现在形参列表的最后,如果出现在形参列表的中间位置,则可以在实参列表中借助于关键字参数解除歧义。
27.简述值传递和引用传递的作用
值传递:实参是不可变数据类型
引用传递:实参是可变的数据类型
28.阅读下面的代码,写出执行结果
def func(): a = [] for i in range(5): a.append(lambda x:i * x) return a result = func() print(result[0](2)) print(result[1](2)) print(result[2](2)) print(result[3](2)) print(result[4](2))
分析:result = func()调用函数func()产生了五个匿名函数,并且存放到列表a当中,后面的五句代码是在调用存在列表a当中的五个匿名函数,由于第一次调用func()的时候i已经变成了4,所以五个函数中的i都是4,则结果为:8, 8, 8, 8, 8
29.什么是匿名函数,优缺点
是一个lambda表达式,本质上还是一个函数,可以设置参数,也可以进行调用,表达式本身就是函数运算的结果
优点:简化代码、减少内存空间的使用
缺点:只能实现简单的逻辑,逻辑一旦复杂,代码的可读性会降低,则不建议使用
30.全局变量的使用场景
问题:当全局变量和局部变量重名,在函数内部对变量直接进行运算,因为系统不能识别是对那个变量进行运算,所以报错
解决:使用global对变量进行声明,表示参与运算的变量来自全局
a = 28 def func(): global a a += 1 func() print(a)
31.简述可迭代对象和迭代器之间的区别和联系
区别:
可迭代对象:Interable,可以直接作用于for循环,如:list,tuple,tr,dict,set,生成器等 迭代器:Iterator,可以直接作用于for循环和next的数据,如:生成器
联系:
迭代器一定是可迭代对象,可迭代对象不一定是迭代器但是,可以通过iter()将不是迭代器的可迭代对象转换位迭代器
32.代码阅读题
def func(li[]): li.append("abc") return li print(func()) # 调用func()函数,会往默认列表li里面添加"abc" 结果为["abc"] print(func([3])) # func([3]),表示传入新的列表,列表里面已经有元素3, 所以结果为[3, "abc"] print(func()) # 再次调用func()函数,会在之前调用之后li列表的基础上, # 再次添加"abc" 结果为:["abc", "abc"] print(func([3])) # 传入新的列表,注意:虽然里面元素跟前面一样,但这是两个不同的列表。 # 所以结果为[3,"abc"]
33.需求:书写一个装饰器,可以统计任意一个函数的执行时间
import time def get_time(func): inner(*args,**kwargs): # 开始时间 start_time = time.time() func(*args,**kwargs) # 结束时间 end_time = time.time() return round(end_time - start_time, 3) # 返回内部函数的引用 return inner @get_time def test(): for _ in range(10**8): pass return 100
34.斐波那契数列(递归)
def func(n): if n == 1 or n == 2: return 1 else: return func(n-1) + func(n-2)
35.简述变量的作用域有哪些,全局变量和局部变量有什么区别
变量作用域分类:
全局作用域:Global 函数作用域:Enclosing 局部作用域:ocal 内置作用域:Built-in
全局变量:定义在函数的外部的变量,可以在整个程序中被访问
局部变量:定义在函数内部的变量,仅限于在该函数的内部被访问
36.简述类属性和实例属性的区别和联系/简述类的字段和对象的字段的区别和联系
1.定义的位置不同:类属性直接定义在类中,实例属性:动态绑定或者定义在构造函数中
2.访问方式不同:类属性可以通过对象或者类名访问,实例属性只能通过对象访问
3.在内存中出现的实际不同:类属性随着类的加载而出现,实例属性随着对象的创建而出现
4.优先级不同:当类属性和实例属性重名的时候,使用对象优先访问实例属性
5.使用场景不同:如果是多个对象共享的数据则定义位类属性,如果是对象特有的数据,则定义位实例属性。
37.new和init的工作原理
1.当代码执行变量 = 类名() 语法是,可以创建一个对象出来,主要是因为先调用了new,然后调用了init
2.创建对象的时候会自动调用new,该函数的返回值就是被当前创建的对象【实例】
3.当对象创建完毕之后,会自动调用init,在该函数内部给对象赋初始值
4.不管是new还是init是否显式的书写在类中,永远都是先调用new,然后再调用init
5.首先将指定类传参给new,创建一个对象并返回,然后将该对象传参给init,则可以给对象设置初始值
38.构造函数init和普通函数的区别
不同点:
1.函数名不同:实力函数的函数名可以自定义,init的函数名是固定的
2.调用不同:init是在创建对象的过程中被自动调用的,而实例函数必须手动调用
3.调用次数:对于同一个对象而言,init一般只会被调用一次,而实例函数可以根据需求调用多次
相同点:
1.二者都定义在类中,形参列表的第一个参数都是self,二者都可以被称为实例函数。
2.形参列表可以设置为默认参数,不定长参数,实参列表也可以使用关键字参数
3.二者都可以根据需求设置返回值
39.面向对象的特征是什么,怎么使用,有什么作用?
封装:
a.将不希望被外界直接访问的属性进行私有化,在定义属性的时候在属性名前家两个下划线
作用:为了提高数据的安全性,为了提高数据的复用性
b.封装式定义类的准则【根据各个对象的特点,将相同的属性和行为提取出来一个新的类,还可以进行属性私有化】
继承:
a.将多个相似类中的属性和函数提取出来,形成一个父类,子类将会继承父类中未被私有化的属性和函数
作用:简化代码,提高了代码的复用性,提高了代码的可扩展性
b.继承是设计类的技巧【父类和子类,为了代码的复用性】
多态:
a.在继承的前提下,当代码运行的时候,根据传入的对象确定该对象是什么类型,可以调用哪个函数
作用:增加代码的灵活性
b.多态式调用函数的技巧【不同的子类可以调用不同的函数,产生不同的结果】
40.代码阅读题
@property装饰器装饰类函数,表示将函数转换为属性使用
阅读下面代码,写出执行结果
class Person(boject): @property def show(self): print("show") p = Person() print(p.show)
分析:p.show表示对象p调用函数show,控制台会打印show,然后print(p.show)表示打印show函数的返回值,而show函数返回值为None所以打印结果为:
show
None
41.解释下面不同书写形式的属性的含义
a:普通属性/公开属性,在类的内外可以直接访问
_a:一般称为保护属性,在类的内外都可以直接访问,但是,一般不建议使用
__a:私有属性,只能在类的内部被访问,在类的外面一般借助于暴露的函数
a:自定义的变量不建议使用 ,一般体现为系统属性或者系统函数的用法,如:init、new、str、repr
42.继承的特点和优点
继承的特点:
1.子类对象可以直接访问父类中为被私有化的属性
2.子类对象可以直接调用父类中为被私有化的函数
3.父类对象不能访问子类中特有的属性和函数
继承的优点:
1.可以简化代码
2.提高了代码的可复用性
3.提高了代码的可扩展性
4。继承是多态的前提
43.类属性
class Mclass1(object): x = 10 class Myclass2(Mclass1): pass class Myclass3(Myclass1): pass print(Myclass1.x,Myclass2.x,Myclass3.x) Myclass2.x = 20 print(Myclass1.x,Myclass2.x,Myclass3.x) Mclass1.x = 30 print(Myclass1.x,Myclass2.x,Myclass3.x)
分析:
第一次打印:都继承自父类Myclass1的属性,打印结果为:10 10 10
第二次打印:Myclass2修改了属性的指向,Myclass3还是继承自父类Myclass1的属性,打印结果为:10 20 10
打三次打印:父类修改类指向,Myclass2指向是属于自己定义的指向,不会继承父类的属性,所以打印结果为:30 20 30
44.继承树/菱形冲突
结论:
在多继承中,如果多个类之间的继承关系比较复杂,则被称为继承树或者菱形冲突
在多继承中,如果在中间某层有向上解析的迹象,则按照父类列表中弗雷德排序顺序进行广度排序
45.简述实例函数、类函数、静态函数之间的区别和联系
相同点:
本质上都是函数,所以默认参数,关键字参数,不定长参数都可以正常使用,也可以设置返回值
不同点:
1.是否有装饰器:类函数需要@classmethod装饰器修饰,静态函数需要staticmethod装饰器修饰,实例函数不需要装饰器修饰。
2.参数列表不同:类函数第一个参数必须为cls,实例函数第一个参数必须为self,静态函数对参数列表没有要求
3.调用方式不同:类函数和静态函数都可以使用类名或者对象调用,但实例函数只能通过实例对象调用
4.使用场景不同:
a.如需要在函数内部创建当前类的对象,则选择类函数 b.如果需要封装一个工具类,为了简化函数的调用,建议使用类函数或者静态函数 c.如需要在函数内部获取对象的属性,则选择实例函数
46.统计创建对象的个数
方式一:new
class Person(object): __per_count = 0 def __new__(cls,*args**kwargs): print("new方法被调用了") cls.__per_count += 1 return object.__new__(cls) def __init__(self): print("init被调用了") @classmethod def get_count(): return cls.__per_count
方式二:init
class Person(object): __per_count = 0 @classmethod def __init__(cls): print("hello",cls) cls.__per_count += 1 @classmethod def get_count(cls): return cls.__per_count
47.什么是单例设计模式?
定义一个普通类,该类可以创建无数个对象,定义一个类,让该类只能创建一个对象,该类被称为单例类,单例:单个实例【对象】
程序运行过程中,确保某一个类只有一个实例【对象】,不管在哪个模块去这个类的对象,获取到的都是同一个对象。该类有一个静态方法,像整个工程提供这个实例,例如:一个国家只有一个主席
单例设计模式的核心:一个类有且只有一个实例,并且这个实例需要应用于整个程序中,该类被称为单例类
48.两种实现方式单例设计模式
类属性实现单例设计模式
""" 设计模式 经过总结,优化,对我们经常遇到的问题所提出的一些可重用的方案 设计模式是一种必须在特定的情境下使用的一种解决问题的犯案,不绑定编程语言 23种设计模式,常用的单例,MVC(MTV),生产者消费者设计模式 单例设计模式: 单例:单个实例,单个对象 程序在运行过程中,确保一个类只能创建一个实例,不管在当前程序中哪个模块中获取该类对象,获取到的都是同一个对象 单例设计模式的核心:一个类仅有一个实例,该实例还需被应用在整个程序中 """ class Person(object): __instance = None def __new__(cls, *args, **kwargs): if not cls.__instance: cls.__instance = super(Person, cls).__new__(cls) return cls.__instance class A(object): """ 每次创建对象,都会调用__new__和__init__,只有第一次调用时__init__是动态绑定属性, 后面调用都是对属性的重新赋值 """ __isinstance = None def __new__(cls, *args, **kwargs): if cls.__isinstance is None: cls.__isinstance = super(A, cls).__new__(cls) return cls.__isinstance def __init__(self, name, age): self.name = name self.age = age a1 = A('jack', 12) a2 = A('tom', 13) print(id(a1)) print(id(a2))
装饰器实现单例设计模式
# 1.装饰器装饰类 def singleton(cls): instance = None def getinstance(*args, **kwargs): nonlocal instance # 构造函数只会在第一次创建对象的时候被调用 if instance is None: instance = cls(*args, **kwargs) return instance return getinstance @singleton class A(object): pass a1 = A() a2 = A() print(id(a1)) print(id(a2))
49.异常
当函数中使用了try_except_finally语句,如果在try或者except中使用return语句,finally不受影响,仍然会执行
def test(): try: num = = int(input("请输入一个数字:")) list1 = [2,2,3] print(list1[num]) return except ValueErroy as e: print(e) except IndexErroy as e: print(e) finally: print("finally执行了")
50.with 上下文件管理异常工作原理
class Check(object): def __enter__(self): # 当对象进入上下文件管理时,主要是为了初始化相应的资源,如,打开文件 print("enter") return self def __exit__(self,exc_type,exc_val,exc_tb): print("exit") # exc_type异常类型,exc_val异常值 # 如果出现异常,系统内部会自己处理,不会向Python解释器抛出异常 return True with Check() as c: print(c)
51.闭包
当一个嵌套函数在其外部区域引用了一个值时,该嵌套函数就是一个闭包,以下代码输出值为:
链接:https://www.nowcoder.com/questionTerminal/d173f0f517de45cb88eff23f8901f2d1?toCommentId=8049851
来源:牛客网
def adder(x): def wrapper(y): return x + y return wrapper adder5 = adder(5) print(adder5(adder5(6)))
解析:
1.得出adder5的结果:
adder5 = adder(5) —> x=5, 并返回wrapper的引用
2.由adder5得出adder5(adder5(6))的结果:
adder5(adder5(6))
----->x=5,wrapper(x=5,wrapper(6))
----->x=5,wrapper(11)
----->5 + 11 = 16