python面试题

1.python中的可变类型和不可变类型
python中的可变数据类型:列表list和字典dict和集合set;不可变数据类型:整型int、浮点型float、字符串型string和元组tuple。
用一句话来概括上述过程就是:“python中的不可变数据类型,不允许变量的值原地发生变化,如果改变了变量的值,相当于是新建了一个对象,而对于相同的值的对象,在内存中则只有一个对象,内部会有一个引用计数来记录有多少个变量引用这个对象;可变数据类型,允许变量的值原地发生变化,即如果对变量进行append、+=等这种操作后,只是改变了变量的值,而不会新建一个对象,变量引用的对象的地址也不会变化,不过对于相同的值的不同对象,在内存中则会存在不同的对象,即每个对象都有自己的地址,相当于内存中对于同值的对象保存了多份,这里不存在引用计数,是实实在在的对象
2.python中的is和==
== 是比较两个对象的内容是否相等,即两个对象的“值“”是否相等。而is 比较的是两个实例对象是不是完全相同,它们是不是同一个对象,占用的内存地址是否相同。
3.字符串驻留intern机制
值同样的字符串对象仅仅会保存一份,是共用的,这也决定了字符串必须是不可变对象。

a = ‘opq’
b = ‘o’ + ‘pq’
id(a)
66832910

id(b)
66832910
intern机制的优点是,在创建新的字符串对象时,会先在缓存池里面找是否有已经存在的值相同的对象(标识符,即只包含数字、字母、下划线的字符),如果有,则直接拿过来用(引用),避免频繁的创建和销毁内存,提升效率。 缺点是在拼接字符串时,或者在改动字符串时会极大的影响性能。原因是字符串在Python当中是不可变对象,所以对字符串的改动不是inplace(原地)操作,需要新开辟内存地址,新建对象。这也是为什么拼接字符串的时候不建议用‘+’而是用join()。join()是先计算出全部字符串的长度,然后再一一拷贝,仅仅创建一次对象。

a = ‘hello world’
b = ‘hello world’
id(a)
66834385

id(b)
66439216
4.连接字符串用join还是+
join的性能明显好于+。这是为什么呢?
原因是这样的,上一篇Python面试之可变对象和不可变对象中讲过字符串是不可变对象,当用操作符+连接字符串的时候,每执行一次+都会申请一块新的内存,然后复制上一个+操作的结果和本次操作的右操作符到这块内存空间,因此用+连接字符串的时候会涉及好几次内存申请和复制。而join在连接字符串的时候,会先计算需要多大的内存存放结果,然后一次性申请所需内存并将字符串复制过去,这是为什么join的性能优于+的原因。所以在连接字符串数组的时候,我们应考虑优先使用join。
5.理解__new__()和__init__()的区别
在面向对象编程中,实例化基本遵循创建实例对象、初始化实例对象、最后返回实例对象这么一个过程。Python 中的 new 方法负责创建一个实例对象,init 方法负责将该实例对象进行初始化.
__new__方法用于创建对象并返回对象,当返回对象时会自动调用__init__方法进行初始化。__new__方法是静态方法,而__init__是实例方法。
new__是一个静态方法,而__init__是一个实例方法.
new__方法会返回一个创建的实例,而__init__什么都不返回.
只有在__new__返回一个cls的实例时后面的__init__才能被调用.
当创建一个新实例时调用__new
,初始化一个实例时用__init
.
6.深拷贝和浅拷贝之间的区别是什么?
深拷贝就是将一个对象拷贝到另一个对象中,这意味着如果你对一个对象的拷贝做出改变时,不会影响原对象。在Python中,我们使用函数deepcopy()执行深拷贝,导入模块copy.

import copy
l = {‘a’: [1,2,3], ‘b’:[4,5,6]}
c = copy.deepcopy(l)
id(l) == id©
False

l[‘a’].append(‘4’)
c[‘b’].append(‘7’)
l
{‘a’: [1, 2, 3, ‘4’], ‘b’: [4, 5, 6]}

c
{‘a’: [1, 2, 3], ‘b’: [4, 5, 6, ‘7’]}
浅拷贝则是将一个对象的引用拷贝到另一个对象上,所以如果我们在拷贝中改动,会影响到原对象。我们使用函数copy()执行浅拷贝.

import copy
l = {‘a’: [1,2,3], ‘b’:[4,5,6]}
c = copy.copy(l)
id(l) == id©
False

l[‘a’].append(‘4’)
c[‘b’].append(‘7’)
l
{‘a’: [1, 2, 3, ‘4’], ‘b’: [4, 5, 6, ‘7’]}

c
{‘a’: [1, 2, 3, ‘4’], ‘b’: [4, 5, 6, ‘7’]}
7.python中的自省机制
自省运行时能够获取对象的类型,python中比较常见的自省(introspection)机制(函数用法)有: dir(),type()返回对象的类型, hasattr(), isinstance(),通过这些函数,我们能够在程序运行时得知对象的类型,判断对象是否存在某个属性,访问对象的属性。
isinstance()测试对象,以确定它是否是某个特定类型或定制类的实例。

isinstance(“python”, str)
True
hasattr(obj, attr): 这个方法用于检查obj是否有一个名为attr的属性,返回一个布尔值。
getattr(obj, attr): 调用这个方法将返回obj中名为attr的属性的值。
setattr(obj, attr, val): 调用这个方法将给obj的名为attr的属性赋值为val。例如如果attr为’name’,则相当于obj.name= val。
dir([obj]): 调用这个方法将返回参数obj的属性和方法列表,obj的默认值是当前的模块对象。我们自定义的属性和方法一般是排在返回列表的末尾(如上例中dir(obj)的最后一行)。
8.生成器和迭代器
通过列表生成式,可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含百万元素的列表,不仅是占用很大的内存空间,如:我们只需要访问前面的几个元素,后面大部分元素所占的空间都是浪费的。因此,没有必要创建完整的列表(节省大量内存空间)。在Python中,我们可以采用生成器:边循环,边计算的机制—>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 at 0x0000028F8B774200>
9.python中的垃圾回收机制
10.filter()、map()、reduce()
filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。该接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判,然后返回 True 或 False,最后将返回 True 的元素放到新列表中。python3中返回迭代器对象。
a=[1,2,3,4,5,6,7,8]
b=filter(lambda x:x>5,a)
print(b)
for i in b:
print(i)
<filter object at 0x0000000001DE5438>
6
7
8

map()是对一个序列的每个项依次执行函数

a = map(lambda x:x*2,[1,2,3])
list(a)
[2, 4, 6]
reduce函数是对一个序列的每个项迭代调用函数,下面是求3的阶乘:

reduce(lambda x,y:x*y,range(1,4))
6

  1. args and **kwargs
    当你不确定你的函数里将要传递多少参数时你可以用
    args.例如,它可以传递任意数量的参数:

def print_everything(*args):
for count, thing in enumerate(args):
… print ‘{0}. {1}’.format(count, thing)

print_everything(‘apple’, ‘banana’, ‘cabbage’)

  1. apple
  2. banana
  3. cabbage
    **kwargs允许你使用没有事先定义的参数名:

def table_things(**kwargs):
… for name, value in kwargs.items():
… print ‘{0} = {1}’.format(name, value)

table_things(apple = ‘fruit’, cabbage = ‘vegetable’)
cabbage = vegetable
apple = fruit
12.python中的装饰器
装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。(即为已经存在的函数或对象添加额外的功能)
举例:

装饰器就是把其他函数作为参数的函数

def log(func):
# # 在函数里面,装饰器在运行中定义函数: 包装.
# 这个函数将被包装在原始函数的外面,所以可以在原始函数之前和之后执行其他代码.
def wrapper(*args, **kw):
print ‘call %s():’ % func.name
return func(*args, **kw)
print ‘after call %s()’ % func.name
# 装饰器的返回值刚才包装过的函数
return wrapper
@log
def demo():
print(“xxx”)
demo()
output:
call demo():
xxx
after call demo()
简单装饰器
http://www.runoob.com/w3cnote/python-func-decorators.html
13.python不支持重载
为了考虑为什么 python 不提供函数重载,首先我们要研究为什么需要提供函数重载。
函数重载主要是为了解决两个问题。
1。可变参数类型。2。可变参数个数。
另外,一个基本的设计原则是,仅仅当两个函数除了参数类型和参数个数不同以外,其功能是完全相同的,此时才使用函数重载,如果两个函数的功能其实不同,那么不应当使用重载,而应当使用一个名字不同的函数。
好吧,那么对于情况 1 ,函数功能相同,但是参数类型不同,python 如何处理?答案是根本不需要处理,因为 python 可以接受任何类型的参数,如果函数的功能相同,那么不同的参数类型在 python 中很可能是相同的代码,没有必要做成两个不同函数。
那么对于情况 2 ,函数功能相同,但参数个数不同,python 如何处理?大家知道,答案就是缺省参数。对那些缺少的参数设定为缺省参数即可解决问题。因为你假设函数功能相同,那么那些缺少的参数终归是需要用的。
好了,鉴于情况 1 跟 情况 2 都有了解决方案,python 自然就不需要函数重载了。
转自知乎:https://www.zhihu.com/question/20053359
14.单例模式
​ 单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案
1.通过模块
2.使用__new__:(使用__new__来控制实例的创建过程)通俗易懂的一种方式:https://blog.csdn.net/CtyCt_/article/details/79677125
3.使用装饰器:装饰器维护一个字典对象instances,缓存了所有单例类,只要单例不存在则创建,已经存在直接返回该实例对象。
def getDeroctor(clas):
instances={}
def warrper(*args,**kwargs):
if clas not in instances:
instances[clas]=clas(*args,*kwargs)
return instances[clas]
return warrper
@getDeroctor
class Foo():
def init(self,name):
self.name=name
c1=Foo(‘Lily’)
c2=Foo(‘Hong’)
print(c1.name,c2.name) //Lily Lily
print(c1 is c2) //True
4.使用静态方法
15.python如何实现多线程
16.解释Python中的join()和split()函数
Join()能让我们将指定字符添加至字符串中。

‘,’.join(‘12345’)
运行结果:
‘1,2,3,4,5’
Split()能让我们用指定字符分割字符串。

‘1,2,3,4,5’.split(’,’)
运行结果:
[‘1’, ‘2’, ‘3’, ‘4’, ‘5’]
17.关于遍历字典的知识
https://blog.csdn.net/jasonlee_lijiaqi/article/details/79193229
现有字典 d= {‘a’:24,‘g’:52,‘i’:12,‘k’:33}请按value值进行排序?
sorted(d.items(),key=lambda x:x[1])
输出:[(‘i’, 12), (‘a’, 24), (‘k’, 33), (‘g’, 52)]
d.items()实际上是将d转换为可迭代对象,迭代对象的元素为 (‘a’,24)、(‘g’,52)、(‘i’,32)、(‘i’,12),(‘k’,33)items()方法将字典的元素 转化为了元组,而这里key参数对应的lambda表达式的意思则是选取元组中的第二个元素作为比较参数(如果写作key=lambda item:item[0]的话则是选取第一个元素作为比较对象,也就是key值作为比较对象。lambda x:y中x表示输出参数,y表示lambda 函数的返回值),所以采用这种方法可以对字典的value进行排序。注意排序后的返回值是一个list,而原字典中的名值对被转换为了list中的元组。
18.元组的解封装
//封装
mytuple=3,4,5
print(mytuple) #(3, 4, 5)
//解封装
x,y,z=mytuple
print(x+y+z) #12
19
将字符串 “k:1 |k1:2|k2:3|k3:4”,处理成字典 {k:1,k1:2,…}
str=“k:1 |k1:2|k2:3|k3:4”
def str2dict(str1):
dict1={}
for items in str.split(’|’):
key,values=items.split(’:’)
dict1[key]=values
print(dict1)
{‘k’: '1 ', ‘k1’: ‘2’, ‘k2’: ‘3’, ‘k3’: ‘4’}
请按alist中元素的age由大到小排序
alist = [{‘name’:‘a’,‘age’:20},{‘name’:‘b’,‘age’:30},{‘name’:‘c’,‘age’:25}]
def sortByAge(alist):
return sorted(alist,key=lambda x:x[‘age’])

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值