Python问答
什么是python迭代器?
迭代器是可以遍历或迭代的对象。
python中的生成器是什么?
返回可迭代项集的函数称为生成器。
可迭代对象
Python中任意的对象,只要它定义了可以返回一个迭代器的__iter__
方法,或者定义了可以支持下标索引的__getitem__
方法(这些双下划线方法会在其他章节中全面解释),那么它就是一个可迭代对象。简单说,可迭代对象就是能提供迭代器的任意对象。
迭代器
任意对象,只要定义了next
(Python2) 或者__next__
方法,它就是一个迭代器。
迭代
用简单的话讲,它就是从某个地方(比如一个列表)取出一个元素的过程。当我们使用一个循环来遍历某个东西时,这个过程本身就叫迭代。
生成器
生成器也是一种迭代器,但是只能对其迭代一次。这是因为它们并没有把所有的值存在内存中,而是在运行时生成值。通过遍历来使用它们,要么用一个“for”循环,要么将它们传递给任意可以进行迭代的函数和结构。大多数时候生成器是以函数来实现的。然而,它们并不返回一个值,而是yield
一个值。
为什么使用* args,** kwargs?
当我们不确定将多少个参数传递给函数,或者我们想要将存储的列表或参数元组传递给函数时,我们使用* args
。当我们不知道将多少关键字参数传递给函数时使用** kwargs
,或者它可以用于将字典的值作为关键字参数传递。标识符args
和kwargs
是一个约定,你也可以使用* bob
和** billy
。
def test_args_kwargs(arg1, arg2, arg3):
print("arg1:", arg1)
print("arg2:", arg2)
print("arg3:", arg3)
使用*args
:
>>> args = ("two", 3, 5)
>>> test_args_kwargs(*args)
arg1: two
arg2: 3
arg3: 5
使用**kwargs
:
>>> kwargs = {"arg3": 3, "arg2": "two", "arg1": 5}
>>> test_args_kwargs(**kwargs)
arg1: 5
arg2: two
arg3: 3
__init__?
__init__
是Python中的方法或者结构。在创建类的新对象/实例时,将自动调用此方法来分配内存。所有类都有__init__
方法。
三元表达式?
condition_is_true if condition else condition_is_false
Map、Filter和Reduce?
Map
Map
会将一个函数映射到一个输入列表的所有元素上。
map(function_to_apply, list_of_inputs)
大多数时候,使用匿名函数(lambdas)来配合map
。
items = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, items))
Filter
filter
过滤列表中的元素,并且返回一个由所有符合要求的元素所构成的列表,符合要求
即函数映射到该元素时返回值为True。
Reduce
当需要对一个列表进行一些计算并返回结果时,Reduce
是个非常有用的函数。举个例子,当你需要计算一个整数列表的乘积时。
通常在 python 中你可能会使用基本的 for 循环来完成这个任务。
from functools import reduce
product = reduce( (lambda x, y: x * y), [1, 2, 3, 4] )
# Output: 24
Lambda?
lambda
表达式是一行函数。它们在其他语言中也被称为匿名函数。如果不想在程序中对一个函数使用两次,可以采用lambda表达式,它们和普通的函数完全一样。
lambda 参数:操作(参数)
列表排序
a = [(1, 2), (4, 1), (9, 10), (13, -3)]
a.sort(key=lambda x: x[1])
print(a)
# Output: [(13, -3), (4, 1), (1, 2), (9, 10)]
装饰器?
即修改其他函数的功能的函数。有助于让代码更简短。
在函数中定义函数
def hi(name="yasoob"):
print("now you are inside the hi() function")
def greet():
return "now you are in the greet() function"
def welcome():
return "now you are in the welcome() function"
print(greet())
print(welcome())
print("now you are back in the hi() function")
hi()
#output:now you are inside the hi() function
# now you are in the greet() function
# now you are in the welcome() function
# now you are back in the hi() function
# 上面展示了无论何时你调用hi(), greet()和welcome()将会同时被调用。
# 然后greet()和welcome()函数在hi()函数之外是不能访问的,比如:
greet()
#outputs: NameError: name 'greet' is not defined
从函数中返回函数
def hi(name="yasoob"):
def greet():
return "now you are in the greet() function"
def welcome():
return "now you are in the welcome() function"
if name == "yasoob":
return greet
else:
return welcome
a = hi()
print(a)
#outputs: <function greet at 0x7f2143c01500>
#上面清晰地展示了`a`现在指向到hi()函数中的greet()函数
#现在试试这个
print(a())
#outputs: now you are in the greet() function
在if/else
语句中我们返回greet
和welcome
,而不是greet()
和welcome()
。为什么那样?这是因为当你把一对小括号放在后面,这个函数就会执行;然而如果你不放括号在它后面,那它可以被到处传递,并且可以赋值给别的变量而不去执行它。
当我们写下a = hi()
,hi()
会被执行,而由于name
参数默认是yasoob,所以函数greet
被返回了。如果我们把语句改为a = hi(name = "ali")
,那么welcome
函数将被返回。我们还可以打印出hi()()
,这会输出now you are in the greet() function。
将函数作为参数传给另一个函数
def a_new_decorator(a_func):
def wrapTheFunction():
print("I am doing some boring work before executing a_func()")
a_func()
print("I am doing some boring work after executing a_func()")
return wrapTheFunction
def a_function_requiring_decoration():
print("I am the function which needs some decoration to remove my foul smell")
a_function_requiring_decoration()
#outputs: "I am the function which needs some decoration to remove my foul smell"
a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)
#now a_function_requiring_decoration is wrapped by wrapTheFunction()
a_function_requiring_decoration()
#outputs:I am doing some boring work before executing a_func()
# I am the function which needs some decoration to remove my foul smell
# I am doing some boring work after executing a_func()
装饰器即封装一个函数,并且用各种方式来修改它的行为。
采用@
@a_new_decorator
def a_function_requiring_decoration():
"""Hey you! Decorate me!"""
print("I am the function which needs some decoration to "
"remove my foul smell")
a_function_requiring_decoration()
#outputs: I am doing some boring work before executing a_func()
# I am the function which needs some decoration to remove my foul smell
# I am doing some boring work after executing a_func()
#the @a_new_decorator is just a short way of saying:
a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)
如果我们运行如下代码会存在一个问题:
print(a_function_requiring_decoration.__name__)
# Output: wrapTheFunction
Ouput 输出应该是“a_function_requiring_decoration”。这里的函数被 warpTheFunction
替代了。它重写了我们函数的名字和注释文档(docstring)。Python提供给 functools.wraps
函数来解决这个问题。
from functools import wraps
def a_new_decorator(a_func):
@wraps(a_func)
def wrapTheFunction():
print("I am doing some boring work before executing a_func()")
a_func()
print("I am doing some boring work after executing a_func()")
return wrapTheFunction
@a_new_decorator
def a_function_requiring_decoration():
"""Hey yo! Decorate me!"""
print("I am the function which needs some decoration to "
"remove my foul smell")
print(a_function_requiring_decoration.__name__)
# Output: a_function_requiring_decoration
蓝本规范:
from functools import wraps
def decorator_name(f):
@wraps(f)
def decorated(*args, **kwargs):
if not can_run:
return "Function will not run"
return f(*args, **kwargs)
return decorated
@decorator_name
def func():
return("Function is running")
can_run = True
print(func())
# Output: Function is running
can_run = False
print(func())
# Output: Function will not run
注意:@wraps
接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等的功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性。
类装饰器
由于当我们调用一个对象时,实际上调用的是它的 __call__
方法。因此使用类装饰器还可以依靠类内部的__call__
方法,当使用 @
形式将装饰器附加到函数上时,就会调用此方法。
class Foo(object):
def __init__(self, func):
self._func = func
def __call__(self):
print ('class decorator runing')
self._func()
print ('class decorator ending')
@Foo
def bar():
print ('bar')
bar()
# class decorator runing
# bar
# class decorator ending
内置装饰器
property 装饰器
property
装饰器用于类中的函数,使得我们可以像访问属性一样来获取一个函数的返回值。
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
@property
def stud_info(self):
print(self.name, self.age)
stud = Student("felix", 12)
print(stud.stud_info) # 注意没加括号,即不是stud.get_name()
staticmethod 装饰器
staticmethod
装饰器同样是用于类中的方法,这表示这个方法将会是一个静态方法,意味着该方法可以直接被调用无需实例化,但同样意味着它没有 self
参数,也无法访问实例化后的对象。
classmethod 装饰器
classmethod
依旧是用于类中的方法,这表示这个方法将会是一个类方法,意味着该方法可以直接被调用无需实例化,但同样意味着它没有 self
参数,也无法访问实例化后的对象。相对于 staticmethod
的区别在于它会接收一个指向类本身的 cls
参数。
深拷贝和浅拷贝有什么区别?
在创建新实例类型时使用浅拷贝,并保留在新实例中复制的值。浅拷贝用于复制引用指针,就像复制值一样。这些引用指向原始对象,并且在类的任何成员中所做的更改也将影响它的原始副本。浅拷贝允许更快地执行程序,它取决于所使用的数据的大小。
深拷贝用于存储已复制的值。深拷贝不会将引用指针复制到对象。它引用一个对象,并存储一些其他对象指向的新对象。原始副本中所做的更改不会影响使用该对象的任何其他副本。由于为每个被调用的对象创建了某些副本,因此深拷贝会使程序的执行速度变慢。
所谓浅拷贝就是对引用的拷贝,所谓深拷贝就是对对象的资源的拷贝。
赋值操作:
-
赋值是将一个对象的地址赋值给一个变量,让变量指向该地址。 -
修改不可变对象(str、tuple)需要开辟新的空间 -
修改可变对象(list等)不需要开辟新的空间
-
浅拷贝仅仅复制了容器中元素的地址
a = ['hello', [1, 2, 3]]
b = a[:]
[id(x) for x in a]
# [55792504, 6444104]
[id(x) for x in b]
# [55792504, 6444104]
a[0] = 'world'
a[1].append(4)
print(a)
# ['world', [1, 2, 3, 4]]
print(b)
# ['hello', [1, 2, 3, 4]]
未修改前,a
和b
中元素的地址都是相同的,不可变的hello
和可变的list
地址都一样,说明浅拷贝只是将容器内的元素的地址复制了一份。通过修改后,b
中字符串没改变,但是list
元素随着a
的改变而改变。
-
深拷贝拷贝了一个副本,容器内部元素地址都不相同
from copy import deepcopy
a = ['hello', [1, 2, 3]]
b = deepcopy(a)
[id(x) for x in a]
# [55792504, 55645000]
[id(x) for x in b]
# [55792504, 58338824]
a[0] = 'world'
a[1].append(4)
print(a)
# ['world', [1, 2, 3, 4]]
print(b)
# ['hello', [1, 2, 3]]
深拷贝后,a
和b
的地址以及a
和b
中的元素地址均不同,这是完全拷贝的一个副本,修改a
后,b
没有发生任何改变,因为b
是一个完全的副本,元素地址与a
均不同,a
修改不影响b
。
-
不可变对象:一旦创建就不可修改的对象,包括字符串、元组、数值类型(该对象所指向的内存中的值不能被改变。当改变某个变量时候,由于其所指的值不能被改变,相当于把原来的值复制一份后再改变,这会开辟一个新的地址,变量再指向这个新的地址。) -
可变对象:可以修改的对象,包括列表、字典、集合(该对象所指向的内存中的值可以被改变。变量(准确的说是引用)改变后,实际上是其所指的值直接发生改变,并没有发生复制行为,也没有开辟新的地址,通俗点说就是原地改变。)
转载请注明:XAMPP中文组官网 » Python基础面试问答大全