Python面试题-第二次更新

7.说一下 Python 中的装饰器

原理:利用闭包,将目标函数外面再套一层函数,使得目标函数具有一个新的功能,并且不改变目标函数原有功能。

实现方式:

  1. 闭包

    def decorate(func):
        def wrapper():
            print('新功能')
            func()
            
        return wrapper
    
    def func():
        print('原有功能')
    
    f = decorate(func)
    
    f()
    
    # 结果为:
    新功能
    原有功能
    
  2. @ 语法糖

    def decorate01(func):
        def wrapper():
            print('新功能')
            func()
            
        return wrapper
    
    @decorate01
    def func01():
        print("原有功能")
    
    func01()
    
    # 结果为:
    新功能
    原有功能
    

8.说一说类属性、实例属性、私有属性和保护属性

类属性相当于全部变量,所有由类创建出来的实例,都可以使用,而实例属性相当于局部变量,只能由该实例自己使用,当类属性与实例属性命名一样时,在调用属性该属性时,会屏蔽掉类属性,而是调用实例属性,这点跟 LEGB 很像。

当通过实例对象来修改类属性时,其实修改的并不是类属性,而是新建了一个跟类属性名一样的实例属性。

Python 中将以两个下划线__开头,但不以两个下划线结尾的变量,认作为私有属性,Python 通过 name manage算法,将该私有属性的引用更改为_classname_reference,这样在用户试图调用该私有属性时,会因为对象引用不一样而找不到该属性,故而实现了「属性私有化」。

在获取实例属性时,一般采用定义一个实例方法的方式获取属性,避免直接对实例属性进行操作,起到一个保护属性的作用。

9.为什么说 Python 是动态语言,鸭子类型是指什么

Python 相当于其他静态语言,可以在代码运行过程中,改变变量的属性。

鸭子类型指的是 Python 不用定义变量类型,只要该变量像是什么类型,那么就认为它就是什么类型,我们更多关注的是它的行为,而不是他的类型。

10.元类是什么

实例都是由类创建出来,而类则是由元类创建出来。他们之间的关系相当于「奶奶-妈妈-孙子」。

示例:

class Myclass():
    pass


new = type('NewClass', (Myclass,), {'name': 'new'})

print(new)
print(new.__mro__)  # 查看该类的继承情况

# 结果为
<class '__main__.NewClass'>
(<class '__main__.NewClass'>, <class '__main__.Myclass'>, <class 'object'>)

具体点的内容可以参考这篇问答:什么是元类

11.@staticmethod 和 @classmethod 的区别

@staticmethod 是为类添加一个静态方法

@classmethod 是为类添加一个类方法

12.如何动态添加属性

由于 Python 的特性使得程序在运行过程中,我们可以为某个对象添加属性、方法。

示例:

class Myclass:
    pass


m = Myclass()

# 为实例动态添加一个属性
m.name = 'new_atribute'

def func(self):
    return 'new_function'

# 为实例动态添加一个方法
m.func = func

print(m.name)
print(m.__dict__) # 返回 m 的所有属性,方法

# 结果为
new_atribute
new_function
{'func': <function func at 0x7f9452be12f0>, 'name': 'new_atribute'}

# 另外一种动态添加方法

import types

def func01(self):
	print('new_function01')

# 将实例 m 添加一个属性 func,而这个属性指向func()函数,故当调用 m.func 时,也就相当于调用了 func() 函数,间接实现了为 m 添加方法 func()。
m.func = types.MethodType(func, m)

print(m.func())

# 结果为 new_function01

13.对于迭代器和生成器你知道哪些,它们分别应用于什么场景

先介绍什么是可迭代的Iterable:任何可用于for循环的都是可迭代的。也可以使用collection模块下的isinstance(obj, Iterable)来检验该对象是否可迭代。

示例:

from collections import Iterable


print(isinstance('abc',Iterable)) 
print(isinstance(123,Iterable)) 

# 结果为 True,False

迭代器:任何可以使用next()函数的都是迭代器,也可使用iter()函数将可迭代对象变为迭代器。

示例:

from collections import Iterator


itr = iter('abc')

print(type(itr))
print(isinstance(itr,Iterator)) 

# 结果为 Iterator,True

生成器:任何函数中含有yield关键字的都是生成器,列表生成式中的[]改为()也是一个生成器。

示例:

g = [i for i in range(10)]

print(type(g))

# 结果为 list

g1 = (i for i in range(10))

print(type(g1))

# 结果为 generator

def func():
    for i in range(10):
        yield i

f = func()
print(f)

# 结果为 <generator object func at 0x7f92f5294048>

生成器怎么取值,什么时候结束。

生成器可以通过next(f)f.__next__()f.send()三种方式来取值,

示例:

def func01():
    for i in range(10):
        yield i

f = func()
print(next(f))
print(f.__next__())
print(f.send('hahah'))

# 结果为 
0
1
2

其中f.send()可以向生成器传值,但是其第一次传入的值默认为None。如果想要取出send('hahah')里传入的值,则需要在生成器中添加接收的变量。

示例:

def func():
    for i in range(10):
        # yield i
        temp = yield i
        print(temp)
        
f = func()

print(next(f))
print(f.__next__())
print(f.send('hahah'))  

# 结果为
0
None
1
hahah
2

生成器里的值被取完之后,或者中间遇到 return关键字,就会退出,这三种方法有一个共同点:当生成器取完之后会抛出StopIteration的错误。

而使用for循环来取出生成器里的值就不会抛出错误,这也是最被推荐的。

应用场景:在生成一个包含很多数(百万级别)的列表时,但是又只用得到其中很小一部分的数时,使用列表生成式会极大的浪费内存,且不一定能够生成,因为受机器内存限制。

而使用生成器则不然,生成器只是在你需要的时候,才会申请一块内存,可以边取边用,极大的降低了内存消耗。

生成器用的最多的地方在于「协程」。由于基于 C 解释器下的 Python 中含有一个 GIL 锁,使得 Pyhon 的多线程是一个假性多线程,这也是为什么很多人说 Python 性能慢的原因。

14.说一下进程、线程、以及多任务(多进程、多线程和协程)

  • 进程

    • 概念

      一个程序对应一个进程,这个进程被叫做主进程,而一个主进程下面还有许多子进程。

    • 实现方式

      1. fork()
      2. mutiprocessing.Process
      3. mutiprocessing.Pool
    • 优缺点

      1. fork()是计算机最底层的进程实现方式,一个fork()方法创建出来的进程有两个:主进程、子进程。

        fork()创建出来的进程,主进程不会等待子进程。

        示例:

        import os
        
        
        print('current_pid :%d' % os.getpid())
        
        res = os.fork()
        
        # 子进程返回的是 0
        if res == 0:
        	print('res: %d' % res)
        	print('sub_pid: %d' % os.getpid())
        
        # 主进程返回的是子进程的 pid
        else:
        	print('main_pid: %d' % os.getpid())
        	print('res:%d' % res)
        
        # 结果为
        current_pid :12775
        main_pid: 12775
        res:12776
        res: 0
        sub_pid: 12776
        
      2. multiprocessing.Process

        multiprocessing模块通过将fork方法封装成一个Process类,该类有一个start()方法,当调用该方法时,会自动调用run()方法,开启一个进程。并且由Process创建出来的进程,可以使用join()方法,使得主进程堵塞,被迫等待子进程。

未写完,下一次更新补上

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值