python必看面试题(一)
python中 == 和 is 的区别是什么?
python对象包含三个基本要素:id(身份标识)、type(数据类型) 和 value(值)。
== 比较操作符,is 统一性操作符。
is和==对对象比较判断的内容不同: == 比较判断两个对象的value是否相等,is比较判断的是两个对象的id(唯一身份标识)是否相同。
深拷贝和浅拷贝
深拷贝指的是复制内容,重新开辟一块内存,浅拷贝指的是两个变量同时指向一个内存ID。
import copy
a = [1, 2, 3, 4, 5]
b = a # 浅拷贝
c = copy.deepcopy(a) # 深拷贝
d = a[:] # 深拷贝
e = copy.copy(a) # 当拷贝对象为可变类型时,为深拷贝,为不可变类型时为浅拷贝。
私有化和Proprety
对于类中定义的私有属性,可以通过定义get()和set()方法在类的外部获取和修改值。但是,在类的外部,程序对类中方法和属性的调用有所不同,方法的调用格式为:实例名.方法名(参数),属性的调用格式为:实例名.属性名。方法和属性的调用格式并不统一,为了可以使用属性的方式调用类中的方法,可以在定义方法前面加上@property装饰器。
class Test(object):
def __init__(self):
self.__age = 100
@property
def num(self):
print("-------getter--------")
return self.__age
@num.setter
def num(self, newage):
print("-------setter--------")
self.__age = newage
t = Test() # 创建实例
t.num = 200 # 修改属性__num的值
print(t.num) # 获取属性__num
python的生成器
生成器一边循环,一遍计算,占用内存小,在使用的时候取值,降低CPU和内存空间,提高效率。一般使用for循环取值。
生成器的2种创建方式:
- 把列表生成式的 [ ] 改成 (),就创建了一个generator
- 函数中包含yield关键字,这个函数及时一个generator,调用函数就创建了一个生成器(generator)对象。
# (1)把列表生成式的 [ ] 改成 (),就创建了一个generator;
L = [x **2 for x in range(5)] # 列表生成式创建列表
g = (x **2 for x in range(5)) # 创建生成器
# (2)函数中包含yield关键字,这个函数及时一个generator,调用函数就创建了一个生成器(generator)对象。
def fib(max): # 定义生成器生成斐波那契数列
n, a, b = 0, 1, 1
while n < max:
yield b
a, b = b, a + b
n += 1
obj = fib(5) # 通过函数调用创建生成器对象。
for i in obj:
print(i)
生成器工作原理 :
- generator能够迭代的关键是它有一个next()方法,工作原理就是通过调用next()方法,直到捕获一个异常;
- 带有yield的生成器同样可以用next()方法调用生成器对象取值,next()有两种调用方式:t.next() 和 next(t)。推荐使用for循环来取值(每执行一次,取生成器里面的一个值)。
- yield相当于return返回一个值,并且记住这个返回的位置,下次迭代时,代码从yield的吓一跳语句开始执行。
- .send()和next()一样,都能让生成器往下一步走(下次遇到yield停),但是send()能传一个值,可以强行修改上一个yield表达式值。
python迭代器
迭代器提供了一种不依赖索引的迭代取值方式。首先明白迭代器和可迭代对象的概念:
- 可迭代对象:内置有__iter__()方法的对象,都成为可迭代对象(str, list, tuple, dict, set)。
- 迭代器对象*:内置有__next__()方法的对象,执行该方法可以不依赖索引值;有内置有__iter__()方法的对象,执行迭代器的__iter__()方法得到的是迭代器本身。
因此,迭代器一定是可迭代对象,可迭代对象不一定是迭代器。执行可迭代对象下的__iter__()方法,返回一个迭代器对象,再通过迭代器对象的__next__()方法取值。
python的for循环
for循环本质为迭代器循环,其工作原理如下 :
- 先调用in后面对象的__iter__()方法,将其编程一个迭代器对象;
- 调用next(迭代器),将得到的返回值赋值给变量;
- 循环往复,直到next(迭代器)抛出异常,for循环自动捕捉异常后结束循环。
带有else的for循环:当for 循环正常结束时,会执行else里面的语句;当被break中断时,不执行else里面的语句。
# 例如查找列表中是否有3的倍数,没有的话打印“没有3的倍数”,有则什么都不打印
# 通常方法需要定义一个布尔型的变量,如下代码
a = [1,2,3,4,5]
has_value = False
for value in a:
if value % 3 == 0:
has_value = True
break
if not has_value:
print("没有3的倍数")
# 上述代码可以实现功能,但是还有一种更加优雅的方式实现,避免了定义布尔型变量
a = [1,2,3,4,5]
for value in a:
if value % 3 == 0:
break
else:
print('没有3的倍数')
闭包
一个函数定义在另一个函数内,且 使用到了外部函数的参数,外部函数返回值为内部函数的函数名,这样的整个代码块称之为闭包。当外部函数参数确定时,内部函数参数可以反复调用。
闭包避免了使用全局变量,此外 ,闭包允许将函数与其所操作的数据关联起来。一般来说,当对象中只有一个方法时,使用闭包是更好的选择。
# 定义一个闭包
def adder(x):
def wrapper(y):
return x + y
return wrapper
adder3 = adder(3)
b = adder3(4) # b的值为7
c = adder3(6) # c的值为9,此步解释了当外部参数x不变时,内部函数可以反复调用
python装饰器
装饰器的本质是把原来的函数装饰成新的函数,并且返回这个函数本身的高阶函数(一个函数作为参数传递给另一个函数,或者一个函数的返回值为另一个函数,满足其一则为高阶函数)。
以计算函数执行时间为例,介绍装饰器的使用。
- 装饰没有参数的函数
import time
import functools
def timer(func): # 装饰器函数
@functools.wraps(func) # 显示为被装饰的函数的函数名
def inner(): # 闭包函数
st = time.time()
func()
print(time.time() - st)
return inner
@timer
def func1(): # 需要计算执行时间的函数
print('in func1')
time.sleep(3)
if __name__ == '__main__':
func1()
- 装饰带有参数的函数
import time
import functools
def timer(func):
@functools.wraps(func) # 显示为被装饰的函数的函数名
def inner(*args, **kwargs): # 闭包函数中输入形参,可以接收全部参数
st = time.time()
func(*args, **kwargs) # 将参数传递给被装饰的函数
print(time.time() - st)
return inner
@timer
def func1(a, b, c): # 需要计算执行时间的函数
print('in func1')
print('a = {}, b = {}, c = {}'.format(a, b, c))
time.sleep(1)
if __name__ == '__main__':
func1(12, 2113, 324)
- 装饰带有返回值的函数
import time
import functools
def timer(func):
@functools.wraps(func) # 显示为被装饰的函数的函数名
def inner(a, b, c):
st = time.time()
ret = func(a, b, c) # 通过ret接收返回值
print(time.time() - st)
return ret # 将返回值返回
return inner
@timer
def func1(a, b, c): # 需要计算执行时间的函数
print('in func1')
time.sleep(1)
return True
if __name__ == '__main__':
a = func1()
print(a)
- 通用装饰器(有参有返回值)
import time
import functools
def timer(func):
@functools.wraps(func) # 显示为被装饰的函数的函数名
def inner(*args, **kwargs):
st = time.time()
ret = func(*args, **kwargs)
print(time.time() - st)
return ret
return inner
@timer
def func1(a, b, c): # 需要计算执行时间的函数
print('in func1')
print('a = {}, b = {}, c = {}'.format(a, b, c))
time.sleep(1)
return a + b + c
if __name__ == '__main__':
a = func1(12, 2113, 324)
print(a)
python内置装饰器有staticmethod, classmethod, property。
pyhton实例方法,类方法,静态方法
类对象:将具有相似属性和方法的对象总结抽象为类对象,可以定义相似的属性和方法,不同实例对象去引用类对象的属性和方法,减少代码的重复率 。
实例对象:又称实例化对象,是通过类对象实例化出来的实例
实例方法:第一个参数必须是实例对象(self,可以传递类的属性和方法),只能由实例对象调用
类方法:使用装饰器@classmethod装饰,第一个参数必须是当前类对象(cls,传递类的属性和方法,不能传递实例的属性和方法)。功能:将类本身作为对象进行操作的方法。
静态方法:使用装饰器 @staticmethod装饰,参数随意(没有self和cls参数),方法体中不能使用类或市里的任何属性和方法。实例对象和类对象都可以调用。功能:主要用来 存放逻辑性代码,逻辑上属于类,但是和类 本身没有关系 ,可以理解为静态方法试了独立的、单纯的函数,仅仅托管于某个类的名称空间中。
class Animal():
__num = 0 # 类属性(已私有化,通过下面num方法访问),所有实例对象共有
def __init__(self, name):
self.__name = name # 私有化实例属性
def __new__(self, *args, **kargs): # 重写__new__()方法,实现创建对象,__num自增1
Animal.__add_num()
return super(Animal, self).__new__(self)
@classmethod
def __add_num(cls): # 类方法,可以通过"类名.方法名"调用
cls.__num += 1
@classmethod
def print_hello(cls):
print('hello, this is a classmethod!')
@property
def name(self): # 实例方法
return self.__name
@property
def num(self): # 通过装饰器为__num提供访问方式
return Animal.__num
def jump(self): # 实例方法
print("I'm {}, i am jumping!")
@staticmethod
def add_ab(a, b): # 静态方法,逻辑性代码,与类本身无关
print(a + b)
if __name__ == '__main__':
a = Animal('dog') # 实例化对象a
b = Animal('cat') # 实例化对象b
Animal.print_hello() # 调用类方法
print(a.num)
print(b.num)