2020 年最全 Python 面试题汇总 (二)
@Author:Runsen
求职季在即,技巧千万条,硬实力才是关键,听说今年疫情大环境不好,更要好好准备才行。于是Runsen在牛客网,Leetcode,九章算法,不断地寻找面试真题,共计100题,包括Python基础,算法,数据库,SQL,Linux。
大四刷题拼offer系列,不拼不行啊。我先刷下牛客网的Python的题目和知识点,适当的记录下自己做
错的题目。
文章目录
- 21、__new__和__init__的区别
- 22、==和is的区别
- 23、*args and **kwargs
- 24、 闭包
- 25、PEP8规范
- 26、ascii、Unicode、utf-8、gbk的区别
- 27、进制转换
- 28、可迭代对象迭代器
- 29、迭代器
- 30、获取命令行参数
- 31、filter、map、reduce
- 32、yield和return的区别
- 33、生成器
- 34、enumerate
- 35、面向对象的三大特性
- 36、super
- 37、魔术方法
- 38、@staticmethod和@classmethod区别
- 39、@property
- 40、内置类属性
21、__new__和__init__的区别
填空题:在Python中__new__ 和 __init__ 的区别主要表现在
-
__new__:创建对象时调用,会返回当前对象的一个实例
-
__init__:创建完对象后调用,对当前对象的一些实例初始化,无返回值
简单总结如下:
1,__new__方法是对象的生成方法,__init__方法是对象的初始化方法。
2,对象生成后,才能初始化。故__new__方法在__init__方法之前调用。
3,__new__方法的第一个参数是cls,指的是类本身,__init__的第一个参数是self,指的是__new__方法生成的对象。
4,__new__方法的其余参数会和生成的对象一起继续传给__init__方法。
5,__new__方法的返回值通常调用其父类的__new__方法生成。
6,__init__方法不能有返回值。
7,__new__较少使用,可以用它实现单例模式,即一个类只能创建一个实例,有时候通过使用单例模式可以极大减少内存的占用。
22、==和is的区别
题目举例:说明Python中==和is的区别。
参考解析:is是判读对象标识符是否一致,而==是判读两个对象的内容是否相等!
x is y 相当于 id(x)==id(y)
==是检查两个对象的内容是否相等,会调用对象的内部__eq__()。
例如下面这段代码,只有数值型和字符串型的情况下,a is b才为True,当a和b是tuple,list,dict或set型时,a is b为False。(下面代码只举了字符串和list)
>>> a = "Runsen" >>> b = "Runsen" >>> a is b True >>> id(a) == id(b) True >>> a == b True >>> c = [1,2,3] >>> d = [1,2,3] >>> c is d False >>> id(c) == id(d) False >>> c == d True23、*args and **kwargs
请问:在Python函数中*args and **kwargs有上面区别?
*args 代表一个元组(数组参数),**kwargs代表一个字典(字典参数)
# 单星号(*): 将所有参数以元祖(tuple)的形式导入 def signal_start(param1,*param2):print ("param1={}".format(param1))print ("*param2={}".format(param2)) signal_start(1,2,3,4,5) # param1=1 # *param2=(2, 3, 4, 5)# 双星号(**)将参数以字典的形式导入 def double_start(param1,**param2):print ("param1={}".format(param1))print ("**param2={}".format(param2))double_start(1,a=2,b=3) # param1=1 # **param2={'a': 2, 'b': 3}24、 闭包
问:说说Python中闭包是什么?
答:可以将闭包理解为一种特殊的函数,这种函数由两个函数的嵌套组成,外函数和内函数。
def 外层函数(参数):def 内层函数():print("内层函数执行", 参数)return 内层函数内层函数的引用 = 外层函数("传入参数") 内层函数的引用()在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。
下面举一个具体的闭包函数的实例,代码如下。
# outer是外部函数 def outer(a):# inner是内函数def inner( b ):#在内函数中 用到了外函数的临时变量print(a+b)# 外函数的返回值是内函数的引用return inner ret = outer(5) #ret = inner ret(10) #15 ret 存了外函数的返回值,也就是inner函数的引用,这里相当于执行inner函数25、PEP8规范
请列出至少5个PEP8规范
-
每一级缩进使用4个空续缩进对齐。
-
每行代码的最大长度限制为79个字符
-
在表达式中避免无关的空格
-
代码更改时,相应的注释也要随之更改。
-
命名要规范,通俗易懂
26、ascii、Unicode、utf-8、gbk的区别
- ascii 是最早美国用的标准信息交换码,把所有的字母的大小写,各种符号用 二进制来表示,共有256中,加入些拉丁文等字符,1bytes代表一个字符
- Unicode是为了统一世界各国语言的不用,统一用2个bytes代表一个字符,可以表达2^16=65556个,称为万国语言,特点:速度快,但浪费空间
- utf-8 为了改变Unicode的这种缺点,规定1个英文字符用1个字节表示,1个中文字符用3个字节表示,特点;节省空间,速度慢,用在硬盘数据传输,网络数据传输,相比硬盘和网络速度,体现不出来的
- gbk 是中文的字符编码,用2个字节代表一个字符
27、进制转换
进制转换以十进制为媒介:十六进制前面加上0x,八进制加上0o,二进制前面加上0b。
| 二进制 | bin(int(x, 8)) | bin(int(x, 10)) | bin(int(x, 16)) | |
| 八进制 | oct(int(x, 2)) | oct(int(x, 10)) | oct(int(x, 16)) | |
| 十进制 | int(x, 2) | int(x, 8) | int(x, 16) | |
| 十六进制 | hex(int(x, 2)) | hex(int(x, 8)) | hex(int(x, 10)) |
28、可迭代对象迭代器
什么是可迭代对象?
可迭代的对象:常见的可以被for循环迭代的一些数据类型都是可迭代的对象,如列表,元组,字典,集合,字符串,生成器,range函数生成的数列等。凡是可作用于for循环的对象都是Iterable可迭代对象类型;
>>> iter(1111) Traceback (most recent call last):File "<stdin>", line 1, in <module> TypeError: 'int' object is not iterable >>> iter("1111") <str_iterator object at 0x000002430E9C2320>这些对象都有一个内置的iter方法,且该方法可以返回一个迭代器对象,当用iter(可迭代对象)调用这个对象时,会返回一个迭代器对象(属于Iterator类)
29、迭代器
什么是迭代器?
凡是可作用于next( )函数的对象都是迭代器(Iterator类型),它们表示一个惰性计算的序列。
集合数据类型如list、tuple、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。
30、获取命令行参数
Python代码如何获取命令行参数?
python中获取命令行参数的方法有很多,早先的sys模块的argv就是其中之一,现在使用比较多的是argparse模块。
sys.argv[0]是Python脚本文件名,sys.argv[1:]之后的才是传入到python脚本中的参数。所有通过命令行传入到脚本中的参数,都是字符串类型!
下面是testargv.py文件中的代码。
import sysif __name__ == "__main__":print(sys.argv)print(sys.argv[0])sum = 0if len(sys.argv) > 1:for i in sys.argv[1:]:sum += int(i) # 强制类型转换成int类型print("sum = ", sum)else:print("No Parameter is passed in!")argparse更易于编写用户友好的命令行界面。程序定义所需要的参数,argparse将找出如何从sys.argv中解析这些参数。
使用步骤:
- import argparse 首先导入模块
- parser = argparse.ArgumentParser() 创建一个解析对象
- parser.add_argument() 向该对象中添加你要关注的命令行参数和选项
- parser.parse_args() 进行解析
下面是testargparse.py的代码。
import argparse# ArgumentParser对象保存了将命令行传入的参数解析成Python数据类型的所以信息 parser = argparse.ArgumentParser(description='Process some integers.') # 调用add_argument,即可将程序的参数填充进ArgumentParser,规定 # ArgumentParser如何将命令行的字符转换成objects parser.add_argument('integers', metavar='N', type=int, nargs='+',help='an integer for the accumulator') parser.add_argument('--sum', dest='accumulate', action='store_const',const=sum, default=max,help='sum the integers (default: find the max)') # 调用parse_args() 可以将上述所有的信息保存并开始使用 args = parser.parse_args() print(args.accumulate(args.integers))31、filter、map、reduce
filter() 相当于过滤器的作用
s=[1,2,3,5,6,8,9,10,25,12,30] # 筛选出3的倍数 # 第一个参数为一个返回True或者False的函数,第二个参数为可迭代对象 # 该函数把可迭代对象依次传入第一个函数,如果为True,则筛选 d=filter(lambda x:True if x % 3 == 0 else False,s) print(list(d)) # [3, 6, 9, 12, 30]map与lambda连用
# 第一个参数为函数,依次将后面的参数传给第一个函数,并执行函数 # 如果有多个参数则,依次将后面的对应传给参数 s=map(lambda x,y:x+y,range(10),range(10)) print(list(s)) # [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] ss=map(lambda x:x*x,range(10)) print(list(ss)) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]reduce与lambda连用,与区别是:map返回的是Map对象,需要用List转化。
from functools import reduce # 开始的时候将可迭代对象的第一个数和第二个数当成x和y # 然后将第一次函数的执行结果当成x,然后再传入一个数当成y # 再执行函数 s=reduce(lambda x,y:x+y,range(101)) print(s) # 相当于0+1+2+……+99+100 # 505032、yield和return的区别
你知道yield和return的区别
相同点:都是返回函数执行的结果
不同点:return 在返回结果后结束函数的运行,而yield 则是让函数变成一个生成器,生成器每次产生一个值(yield语句),函数被冻结,被唤醒后再产生一个值。
例子:求一组数的平方值
# return 实现:def squre(n):ls = [i*i for i in range(n)]return ls for i in squre(5):print(i, end=' ')结果为:0 1 4 9 16
# yield 实现:def squre(n):for i in range(n):yield i*ifor i in squre(5):print(i, end=' ')结果为:0 1 4 9 16
yield和return也很好区别,return就返回值,结束函数,yield只是保存,不会结束函数。想下,在scrapy中,如果将yield改为retrun,那么爬一个就return,也就是爬取一个数据就停止了。
33、生成器
什么是生成器?
在Python中,一边循环一边计算的机制,称为生成器:generator。
因为列表所有数据都在内存中,如果有海量数据的话将会非常耗内存。
创建生成器,只要把一个列表生成式的[]改成(),就创建了一个generator:
>>> L = [x * x for x in range(10)] >>> L [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] >>> g = (x * x for x in range(10)) >>> g <generator object <genexpr> at 0x1022ef630>还有一种方法就是在函数中使用yield关键字,那么这个函数就不再是一个普通函数,而是一个generator。调用函数就是创建了一个生成器(generator)对象。
def gen():yield 1# 返回一个对象,这个对象的值是1 def ret():return 1# 返回一个数字1 g = gen() r = ret() print(g,r) print(next(g))#输出如下 <generator object gen at 0x0000020452E83840> 1 1也是就函数中只要有yield,这个函数就会变成生成器。每次运行到yield的时候,函数会暂停,并且保存当前的运行状态,返回返回当前的数值,并在下一次执行next方法的时候,又从当前位置继续往下走。
34、enumerate
enumerate的意思是什么?如何使用。
在Python中,如果要在迭代列表的同时对迭代次数进行计数,可以使用 enumerate关键字。下面是没有使用enumerate实现的遍历。
Mylist = ['Runsen','为','offer', '而战'] for i in range(len(Mylist )):print(i, Mylist [i])# 输出如下 0 Runsen 1 为 2 offer 3 而战enumerate()提供了强大的功能,例如,当您需要获取索引列表时,它会派上用场:
for i, j in enumerate(Mylist):print(i, j)# 输出如下 0 Runsen 1 为 2 offer 3 而战35、面向对象的三大特性
简述面向对象的三大特性?
-
继承:继承就是继承的类直接拥有被继承类的属性而不需要在自己的类体中重新再写一遍,其中被继承的类叫做父类、基类,继承的类叫做派生类、子类。
-
封装: 封装就是把类中的属性和方法定义为私有的,方法就是在属性名或方法名前加双下划线,而一旦这样定义了属性或方法名后,python会自动将其转换为_类名__属性名(方法名)的格式,在类的内部调用还是用双下划线加属性名或方法名,在类的外部调用就要用_类名__属性名(方法名)。父类的私有属性和方法,子类无法对其进行修改。
-
多态:多态就是不同的对象可以调用相同的方法然后得到不同的结果,有点类似接口类的感觉,在python中处处体现着多态,比如不管你是列表还是字符串还是数字都可以使用+和*。
36、super
简述super关键字?
调用父类(超类)的一个方法,可以使用 super() 函数,super() 函数的一个常见用法是在 init() 方法中确保父类被正确的初始化了,具体的看我写的示例。
''' @Author: Runsen @微信公众号: Python之王 @博客: https://blog.csdn.net/weixin_44510615 @Date: 2020/8/29 '''# 创建一个A类 class A(object):def __init__(self):self.x = 0def A(self):print("This A func")class B(A):# 创建 B 继承 Adef __init__(self):super().__init__()self.y = 1def B(self):print("This B class ")print(self.x, self.y)super().A()if __name__ == '__main__':b = B()# 实例化 Bb.B()# 调用B# 输出如下 This B class 0 1 This A func但是这里需要注意如果出现了"双下划线"的函数中的变量,只有类对象自己能访问,连子类对象也不能访问到这个数据。具体看我很早之前写的博客的示例。
class Father():__money = 1000 #私有变量是继承不了def __action(self): # 父类的私有方法money = 1000print('调用父类的方法')class Son(Father):def action(self):super()._Father__action()print(money) son=Son() son.action() 调用父类的方法 name 'money' is not defined这里的__action由于是"双下划线“,因此money 私有变量是不能用supper继承不了,也就是不可以访问父类中的私有属性方法的变量money。
37、魔术方法
什么是魔术方法?
在Python中的面向对象中有很多魔术方法如:
__init__: 构造函数,在生成对象时调用 __del__: 析构函数,释放对象时使用 __str__: 使用print(对象)或者str(对象)的时候触发 __repr__: 在使用repr(对象)的时候触发,前提是没有__str__。如果有__str__,则没有用。 __setitem__ : 按照索引赋值:每当属性被赋值的时候都会调用该方法:self.__dict__[name] = value __getitem__: 按照索引获取值:当访问不存在的属性时会调用该方法 _delitem__(self,name): 当删除属性时调用该方法 __len__: 获得长度 __cmp__: 比较运算 __call__: 函数调用 __add__: 加运算 __sub__: 减运算 __mul__: 乘运算 __div__: 除运算 __mod__: 求余运算 __pow__: 乘方下面,我只介绍几个常见的。__init__和__init__在上面有谈到,这里不再述说。
1、__str__:用于处理打印实例本身的时候的输出内容。如果没有覆写该函数,则默认输出一个对象名称和内存地址。
2、 __del__ : 析构方法,恰好在对象要被删除之前调用,目的是做一些释放销毁工作。
3、__call__:其实就是调用这个对象的 对象名__call__()方法,可以用来改变实例的内部成员的值。
下面看一个具体示例,区别上面的三种魔术方法。
''' @Author: Runsen @微信公众号: Python之王 @博客: https://blog.csdn.net/weixin_44510615 @Date: 2020/8/29 '''class Student(object):def __init__(self, name, age): # 重写了__init__方法self.name = nameself.age = agedef __str__(self):return "姓名:%s; 年龄:%d" % (self.name, self.age)def __del__(self):# 当对象被销毁时,会自动调用这个方法print('__del__ 方法被调用了')def __call__(self, a, b):self.name = aself.age = bprint('__call__ with ({}, {})'.format(self.name, self.age))def say(self):return "Runsen为Offer而战"s = Student('Runsen', 20) print(s) s("runsen",20) print(s) print(s.say()) del s# 输出如下 姓名:Runsen; 年龄:20 __call__ with (runsen, 20) 姓名:runsen; 年龄:20 Runsen为Offer而战 __del__ 方法被调用了38、@staticmethod和@classmethod区别
说下@staticmethod和@classmethod的区别
其实@staticmethod和@classmethod都是用来声明静态方法的。只不过一个声明静态方法,一个声明类方法。
-
类方法:使用装饰器@classmethod。第一个参数为当前类的对象,通常为cls。实例对象和类对象都可以调用类方法。(不用声明对象就可以调用)
-
静态方法:使用装饰器@staticmethod。没有self和cls参数。方法体中不能使用类或者实例的任何属性和方法。实例对象和类对象都可以调用。
为了方便大家了解两者的差别,以下的示例代码将有助于发现其中的差别:
''' @Author: Runsen @微信公众号: 润森笔记 @博客: https://blog.csdn.net/weixin_44510615 @Date: 2020/8/30 ''' class A():@classmethoddef get_name(cls, name):print(cls) # <class '__main__.A'>print('my name is %s' % name)@staticmethoddef get_age(age):print(f'i am %s years old' % age)if __name__ == '__main__':A.get_name('Runsen') # my name is RunsenA.get_age(20) # i am 20 years old@staticmethod 与 @classmethod在Python中称为 装饰器,用来修饰函数,相当于添加一个额外的功能,比如不再像普通函数那样进行实例化。通过使用装饰器可以让代码更加整洁,易读。用了修饰器之后,也可以进行实例化之后再调用,但是就显得多此一举了。
| a = A() | a.get_name() | a.get_name() | a.get_name() |
| A | 不可用 | A.get_name() | A.get_name() |
39、@property
说下你对@property的理解
@property可以将一个方法的调用变成属性调用。举例说明:平时我们调用数据属性和方法,是这样的
class School():name = "家里蹲大学"def test(self):print("实例方法")@propertydef test_pro(self):print("静态属性")if __name__ == "__main__":s = School()print(s.name)s.test()#输出 # 家里蹲大学# 实例方法如果我们想直接类似于数据属性一样的去调用方法
class School():name = "家里蹲大学"def test(self):print("实例方法")@propertydef test_pro(self):print("静态属性")if __name__ == "__main__":s = School()print(s.name)# 注意返回的函数 千万别加()s.test_pro#输出 # 家里蹲大学# 静态属性40、内置类属性
说下你了解几个内置类属性?
__dict__: 类的属性(包含一个字典,由类的数据属性组成)
__doc__ :类的文档字符串
__name__: 类名
__module__: 类定义所在的模块
__bases__: 类的所有父类构成元素(包含了一个由所有父类组成的元组)
如果你想跟博主建立亲密关系,可以关注博主,或者关注博主公众号“Python之王”,了解一个非本科程序员是如何成长的。
博主ID:润森,希望大家点赞、评论、收藏
总结
以上是生活随笔为你收集整理的2020 年最全 Python 面试题汇总 (二)的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: 五十六、TodoList的三种写法,祭奠
- 下一篇: 2020 年最全 Python 面试题汇