最新消息:XAMPP默认安装之后是很不安全的,我们只需要点击左方菜单的 "安全"选项,按照向导操作即可完成安全设置。

Python基础面试问答大全

XAMPP案例 admin 443浏览 0评论

Python问答

什么是python迭代器?

迭代器是可以遍历或迭代的对象。

python中的生成器是什么?

返回可迭代项集的函数称为生成器。

可迭代对象

Python中任意的对象,只要它定义了可以返回一个迭代器的__iter__方法,或者定义了可以支持下标索引的__getitem__方法(这些双下划线方法会在其他章节中全面解释),那么它就是一个可迭代对象。简单说,可迭代对象就是能提供迭代器的任意对象。

迭代器

任意对象,只要定义了next(Python2) 或者__next__方法,它就是一个迭代器。

迭代

用简单的话讲,它就是从某个地方(比如一个列表)取出一个元素的过程。当我们使用一个循环来遍历某个东西时,这个过程本身就叫迭代。

生成器

生成器也是一种迭代器,但是只能对其迭代一次。这是因为它们并没有把所有的值存在内存中,而是在运行时生成值。通过遍历来使用它们,要么用一个“for”循环,要么将它们传递给任意可以进行迭代的函数和结构。大多数时候生成器是以函数来实现的。然而,它们并不返回一个值,而是yield一个值。

drh000040

为什么使用* args,** kwargs?

当我们不确定将多少个参数传递给函数,或者我们想要将存储的列表或参数元组传递给函数时,我们使用* args。当我们不知道将多少关键字参数传递给函数时使用** kwargs,或者它可以用于将字典的值作为关键字参数传递。标识符argskwargs是一个约定,你也可以使用* 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语句中我们返回greetwelcome,而不是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 参数。

深拷贝和浅拷贝有什么区别?

在创建新实例类型时使用浅拷贝,并保留在新实例中复制的值。浅拷贝用于复制引用指针,就像复制值一样。这些引用指向原始对象,并且在类的任何成员中所做的更改也将影响它的原始副本。浅拷贝允许更快地执行程序,它取决于所使用的数据的大小。

深拷贝用于存储已复制的值。深拷贝不会将引用指针复制到对象。它引用一个对象,并存储一些其他对象指向的新对象。原始副本中所做的更改不会影响使用该对象的任何其他副本。由于为每个被调用的对象创建了某些副本,因此深拷贝会使程序的执行速度变慢。

所谓浅拷贝就是对引用的拷贝,所谓深拷贝就是对对象的资源的拷贝。

赋值操作:

  1. 赋值是将一个对象的地址赋值给一个变量,让变量指向该地址。
  2. 修改不可变对象(str、tuple)需要开辟新的空间
  3. 修改可变对象(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]]

未修改前,ab中元素的地址都是相同的,不可变的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]]

drh00032

深拷贝后,ab的地址以及ab中的元素地址均不同,这是完全拷贝的一个副本,修改a后,b没有发生任何改变,因为b是一个完全的副本,元素地址与a均不同,a修改不影响b

  • 不可变对象:一旦创建就不可修改的对象,包括字符串、元组、数值类型(该对象所指向的内存中的值不能被改变。当改变某个变量时候,由于其所指的值不能被改变,相当于把原来的值复制一份后再改变,这会开辟一个新的地址,变量再指向这个新的地址。)
  • 可变对象:可以修改的对象,包括列表、字典、集合(该对象所指向的内存中的值可以被改变。变量(准确的说是引用)改变后,实际上是其所指的值直接发生改变,并没有发生复制行为,也没有开辟新的地址,通俗点说就是原地改变。)

转载请注明:XAMPP中文组官网 » Python基础面试问答大全

您必须 登录 才能发表评论!