python面向对象面试集锦(1-23)

Posted Jeff的技术栈

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python面向对象面试集锦(1-23)相关的知识,希望对你有一定的参考价值。

1、简述面向对象三大特性

封装:
    封装指的是把一堆数据属性与方法数据放在一个容器中,这个容器就是对象。让对象可以通过 "." 来调用对象中的数据属性与方法属性。
    
继承:
    继承指的是子类可以继承父类的数据属性与方法属性,并可以对其进行修改或使用。
    
多态:
    在python中的多态指的是让多种类若具备类似的数据属性与方法属性,都统一好命名规范,这样可以提高开发者的代码统一性,使得调用者更方便去理解。

2、什么是鸭子类型?

    鸭子类型是多态的一种表现形式
    在python中不会强制性要求所有人的代码都统一规范,不统一也不会报错,若使用抽象类就会使python代码强制统一规范,这样不符合python动态语言的特性。所以让大家都自觉统一好规范,若大家的对象方法都类似的话就一种规范,只要长得像鸭子,就称之为鸭子类型。

3、super作用?

基类、超类
'''
        使用super()可以在子类中调用父类的方法或属性, 可能你会说, 子类本来就可以调用父类中所有非私有的属性或方法,而我现在说的是, 当子类中实现了某个方法, 父类中也有这个方法, 当你调用这个方法时, 既想执行子类的又想执行父类的, 在这种情况下就可以使用super()
'''

4、mro是什么?

mro(): 会把当前类的继承关系列出来
'''
        mro全称Method Resolution Order,指的是方法解析顺序。
        方法调用时就需要对当前类和基类进行搜索以确定方法所在的位置。而搜索的顺序就是所谓的「方法解析顺序」。
'''

5、什么是C3算法?

# 答案
'''
C3算法最早被提出是用于Lisp的,应用在Python中是为了解决原来基于深度优先搜索算法不满足本地优先级,和单调性的问题。
本地优先级:指声明时父类的顺序,比如C(A,B),如果访问C类对象属性时,应该根据声明顺序,优先查找A类,然后再查找B类。
单调性:如果在C的解析顺序中,A排在B的前面,那么在C的所有子类里,也必须满足这个顺序。
'''

6、列举面向对象中带双下线的特殊方法?

类的内置方法(魔法方法):
    凡是在类内部定义,以__开头__结尾的方法,都是类的内置方法,也称之为魔法方法。
    类的内置方法,会在某种条件满足下自动触发。

内置方法如下:
    __new__: 在__init__触发前,自动触发。  调用该类时,内部会通过__new__产生一个新的对象。
    __init__: 在调用类时自动触发。    通过产生的对象自动调用__init__()
    __getattr__: 在 “对象.属性” 获取属性时,若 “属性没有” 时触发。
    __getattribute__: 在 “对象.属性” 获取属性时,无论 "属性有没有" 都会触发。
        # 注意: 只要__getattr__ 与 __getattribute__ 同时存在类的内部,只会触发__getattribute__。
   __setattr__:当 “对象.属性 = 属性值” , 添加或修改属性时触发
   __call__ : 在调用对象 “对象 + ()” 时触发。 即:对象() 或者 类()()
   __str__  : 在打印对象时触发。 # 注意: 该方法必须要有一个 “字符串” 返回值。
   __getitem__: 在对象通过 “对象[key]” 获取属性时触发。
   __setitem__: 在对象通过 “对象[key]=value值” 设置属性时触发。
   __gt__,__lt__,__eq__:自定义比较对象大小双下:gt、lt、eq
   __enter__:  进入文件时,开打文件时执行。返回值:self
   __exit__:  退出文件时,报错中断、或者代码执行完时执行。 返回值:可以有返回值,是bool类型
   __del__ : 手动删除时立马执行,或者程序运行结束时自动执行
            使用场景:当你的对象使用过程中,打开了不属于解释器的资源;例如,文件,网络端口
   __slots__:原理,给对象声明只有某些属性,从而删除不必要的内存,不能添加新属性
            使用场景:1.优化对象内存  2.限制属性数量

7、单下划线与双下划线的作用?

在python中用双下划线开头的方式将属性隐藏起来(设置成私有的)
***原理: 替换变量名称 方法名 替换为:_类名__方法名***

'''
"单下划线" 开始的成员变量叫做保护变量,意思是只有类对象和子类对象自己能访问到这些变量。

"双下划线" 开始的是私有成员,意思是只有类对象自己能访问,连子类对象也不能访问到这个数据。
'''

8、实例变量和类变量的区别?

'''
    类变量是所有对象共有,其中一个对象将它值改变,其他对象得到的就是改变后的结果;而实例变量则属对象私有,某一个对象将其值改变,不影响其他对象;
'''

9、静态方法和类方法区别?

'''
Python的类就是个语法糖。一个函数写在类里面和写在类外面没有区别,唯一的区别就是参数,所谓实例方法就是第一个参数是self,所谓类方法就是第一个参数是class,而静态方法不需要额外的参数,所以必须区分。
'''

10、isinstance、issubclass和type的作用?

issubclass 判断是否是谁的子类
isinstance判断一个对象是否是另一个类的实例化
type获取数据类型
'''
type和isinstance都可以判断变量是否属于某个内建类型
type只接收一个参数,不但可以判断变量是否属于某个类型,而且可以得到参数变量未知的所属的类型;而isinstance只能判断是否属于某个已知类型,不能直接得到变量未知的所属的类型
'''

11、有用过with statement(语句)吗?它的好处是什么?

# 答案
'''
with语句会在嵌套的代码执行之后,自动关闭文件。这种做法的还有另一个优势就是,无论嵌套的代码是以何种方式结束的,它都关闭文件。如果在嵌套的代码中发生异常,它能够在外部exception handler catch异常前关闭文件。如果嵌套代码有return/continue/break语句,它同样能够关闭文件。
'''

12、不可迭代的数据类型和可迭代的数据类型?

可迭代:dict、set、str
不可迭代:object

13、单例模式?singleton

单例模式:一份数据,重复使用。(一个地址),节约内存空间
单例模式: (面试让你手撸,一定要背下来。)
    1.通过classmethod  类绑定方法
    2.通过装饰器实现
    3.通过__new__实现
    4.通过导入模块时实现
    5.通过元类实现。
 
# 答案:
1.__new__
class Borg(object):
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, '_instance'):
            ob = super(Borg, cls)
            cls._instance = ob.__new__(cls, *args, **kwargs)
        return cls._instance
class MyClass(Borg):
    def __init__(self):
        self.a = 1
        
2.共享属性
class Borg2(object):
    _state = {}

    def __new__(cls, *args, **kwargs):
        ob = super(Borg2, cls).__new__(cls, *args, **kwargs)
        ob.__dict__ = cls._state
        return ob

class MyClass(Borg2):
    def __init__(self):
        self.a = 1
        
3.装饰器
def singleton(cls, *args, **kwargs):
    instances = {}

    def getinstance():
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]

    return getinstance

@singleton
class MyClass(object):
    def __init__(self):
        self.a = 1

4.import方法
# mysingleton .py
class MyClass(object):
    def __init__(self):
        self.a = 1

s_myclass = MyClass()

from mysingleton import s_myclass

s_myclass.a

14、python中如何判断一个对象是哦福可调用?

# python 中如何判断一个对象是否可调用
def func():
    pass
print(callable(func))  # True

15、那些对象可以被调用?

# 哪些对象可以是可调用对象
1.类对象
2.所有带有_call_()方法的对象

# 如何定义一个类,使其对象本身就是可调用对象
一个类实例也可以变成一个可调用对象,只需要实现一个特殊方法call()。

16、队列、堆栈?

queue:队列模块

joinablequeue:可等待的队列模块

队列(FIFO):先进先出(买包子,先排队先买)

堆栈(FILO):先进后出(衣柜放衣服,先放进去,后拿出来)

from multiprocessing import Queue

q = Queue(5)  # 生成队列对象,括号内的数字表示 这个队列最大储存数
q.put(1)  # 往队列中添加数据1,

     当队列满了,再放数据,不会报错,程序原地等待(阻塞态),等待有位置放值

q.get()   # 取值,因为先进先出,所以先取第一个值

        当队列取完之后,再次取值,程序原地等待(阻塞态),等待有值取

q.get_nowait()  # 取值不等待(阻塞),没有了直接报错

q.put_nowait()  # 添加值不等待(阻塞),满了直接报错

q.full()  # 判断队列是否满了(bool值)
q.empty()  # 判断队列是否为空(bool值)

q.join()  # 等待
Queue.task_done() 在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号
Queue.join() 实际上意味着等到队列为空,再执行别的操作 
            如果线程里每从队列里取一次,但没有执行task_done(),则join无法判断队列到底有没有结束,在最后执行个join()是等不到结果的,会一直挂起。
可以理解为,每task_done一次 就从队列里删掉一个元素,这样在最后join的时候根据队列长度是否为零来判断队列是否结束,从而执行主线程。

17、关于python类的继承?

 继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类(super),新建的类称为派生类或子类

18、用两个队列来实现一个栈?

# 答案:
class StackWithTwoQueues(object):
    #定义两个空队列
    def __init__(self):
        self.queue1 = []
        self.queue2 = []
    #入栈
    def push(self, item):
        self.queue1.append(item)
    #出栈
    def pop(self):
        if len(self.queue1) == 0:
            return(None)
        while(len(self.queue1) != 1):
            self.queue2.append(self.queue1.pop(0))
        self.queue1, self.queue2 = self.queue2, self.queue1
        return (self.queue2.pop())
#test
if __name__ == '__main__':
    ss = StackWithTwoQueues()
    list = [0, 1, 2, 3, 4]
    for i in range(5):
        ss.push(list[i])
    print(list)
    for i in range(5):
        print(ss.pop(), ',', end = '')
#output
#[0, 1, 2, 3, 4]
#4, 3, 2, 1, 0

19、类的加载顺序?

1、 类对象 
2、 实例对象 
3、 self变量名称问题 
4、 类属性、实例变量、局部变量 
5、 类方法 
6、 实例方法 
7、 类方法与实例方法相互调用 
8、 静态方法 
9、 继承时三类方法的影响 

20、以下代码输出结果?


class Parent:
    x = 1

class Child1(Parent):
    pass

class Child2(Parent):
    pass
print(Parent.x, Child1.x, Child2.x)
Child1.x = 2
print(Parent.x, Child1.x, Child2.x)
Child1.x = 3
print(Parent.x, Child1.x, Child2.x)

# 答案
'''
1 1 1
1 2 1
1 3 1
'''


解释:优先使用自己的x,如果自己没有则使用继承的

21、classmethod类方法和staticmethod静态方法?

classmethod:类绑定方法
    是一个装饰器,给在类内部定义方法中装饰,将类内部的方法变为 “类的绑定方法”。直接类.方法

class Dog():
    def eat(self):
        print('狗狗吃饭饭。。。')

class Pig():
    @classmethod  # 将此方法对象绑定
    def eat(self):
        print('猪猪看电视。。。')

# 对象绑定方法,需要实例化出一个对象
keji = Dog()
keji.eat()

# 类绑定方法,不需要对象,直接通过类
zhu = Pig.eat()


staticmethod:非绑定方法(静态方法)
    - 可以由对象或类来调用,谁来调用都是一个普通方法(普通函数),方法需要传入几个参数,就得传入几个。

22、@property装饰器?

装饰器:为了调用方式一致,方便使用者
@property首先创建一个age对象,所以@setter、@deleter要加函数名
        :这里的age是装饰方法的名称

@property(获取私有) :把一个方法伪装成普通属性,通常函数名和属性名保持一致(方便使用者调用)
@函数名.setter(修改私有):函数名和属性名保持一致
@函数名.deleter(控制删除):
     
class A:
    def __init__(self, name, age):
        self.name = name
        self.__age = age

    @property   # 获取属性时,触发下面
    # 获取伪装成普通属性,通常函数名伪装成属性名
    def age(self):
        return self.__age

    @age.setter  # 修改属性时,触发下面
    def age(self, new_age):
        self.__age = new_age
        print('修改成功')

    @age.deleter  # 删除属性时,触发下面
    def age(self):
        del self.__age
        print('删除成功,已经没有了')
# 查看
jeff = A('jeff', 50)
print(jeff.age)  #  50

# 修改
jeff.age = '100'
print(jeff.age)  # 修改成功   100

# 删除
del jeff.age
print(jeff.age)  # 报错,删除成功,已经没有了

23、栈与队列?

编程实现一个先进先出的队列,能够指定初始化队列大小,以及enqueue,dequeue,isempty,isfull四种方法?
使用方法如下:

s = Queue(2)  # 初始化一个大小为 2 的队列
s.is_empty()  # 初始化后, 队列为空, 返回 True
s.enqueue(1)  # 将 1 加入队列
s.enqueue(2)  # 将 2 加入队列
s.isfull()  # 加入了两个元素, 队列已满, 返回 True 6. s.dequeue() # 移除一个元素, 返回 1
s.dequeue()  # 移除一个元素, 返回 2
s.is_empty()  # 队列已经为空, 返回 True

以上是关于python面向对象面试集锦(1-23)的主要内容,如果未能解决你的问题,请参考以下文章

面向面试编程代码片段之GC

Java面试常见问题集锦

透露一下Java软件工程师面试常见问题集锦之一

python公司面试题集锦 python面试题大全

纯JS制作选项卡--JavaScript实例集锦(初学)

(面试题)python面试题集锦-附答案