1、谈谈你对面向对象的理解?
核心二字为:对象,具体体现是将数据和数据处理的程序封装到对象中
较面向过程而言:可拓展性好,相对面向过程而言,需要经代码一层层的抽离去达到高内聚低耦合的效果,复杂度较高。
2、Python面向对象中的继承有什么特点?
可以继承父类所有静态属性和普通方法,从而提高代码的重用率。
3、面向对象深度优先和广度优先是什么?
二者 是指 属性,方法查找时对父类的查找顺序
深度优先:一条线走到底,没找到答案的话,回头从另外一条路再继续走下去
广度优先:先走例其最近的路,没找到,再走下个最近的路
4、面向对象中super的作用?
super()可以调用父类所有可以被继承的方法和对象属性,使用者是子类对象
super()本质上是 super(子类类名,子类对象)
super().父类普通方法 super().__init__() super().func()
super(当前类名,self).__init__() 等价于 super().__init__()
5、是否使用过functools中的函数?其作用是什么?
1)、
偏函数:from functools import partial
用于其他进制的字符串与十进制数据之间的转换
import functools
def transform(params):
foo = functools.partial(int,base=2)
print(foo(params))
transform('1000000') # 64
transform('100000') # 32
2)、wraps from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
'''decorator'''
print('Decorated function...')
return func(*args, **kwargs)
return wrapper
@my_decorator
def test():
"""Testword"""
print('Test function')
print(test.__name__, test.__doc__)
# 输出结果为 test , Testword
#如果去掉wraps(func),则结果为 wrapper decorator
即:用来保证被装饰函数在使用装饰器时不改变自身的函数名和应有的属性,
避免被别人发现该函数是被装饰器泡过的。
6、列举面向对象中带爽下划线的特殊方法,如:__new__、__init__
__new__ 在实例化对象时触发,即控制着对象的创建
__init__ 在对象创建成功后触发,完成对象属性的初始化
__call__ 在调用对象时触发,也就是对象()时触发
__setattr__ 在给对象赋值时触发,对象的属性若不存在则先创建
__getattr__ 在对象.属性时触发,该属性必须不存在
__mro__ 打印当前类的继承顺序
__dict__ 打印出当前操作对象名称空间的属性和值
__str__ 在打印对象时,触发,执行内部的代码
__doc__ 类的文档字符串
7、如何判断是函数还是方法?
二者都是解决问题的实现功能
函数:通过函数名直接调用
方法:通过附属者的点语法去调用
变量:通过变量名访问变量值
属性:通过附属者.语法去调用
8、静态方法和类方法区别? 实例方法 就是 普通方法
@classmethod
类方法:可以被类与对象调用的方法,第一个参数一定是当前类本身,对象调用的时候,本质上还是通过类去调用该方法的,可以通过id去证明。id(cls)
实例方法就是普通的方法,只是参数列表里的参数不会出现self和cls
@staticmethod
静态方法:直接通过类|实例.静态方法调用。被该方法修饰的函数,不需要self参数和cls参数,使用的方法和直接调用函数一样
逻辑上来说,实例方法类本身和其对象都可以调用,
类方法是专属于类的
静方法可以被类和对象调用,
类方法、静态方法 在被类直接调用时,没有经过实例化的过程,因此可以减少内存资源的占用。
9、列举面向对象中的特殊成员以及应用场景
封装:用于属性和方法的私有,不让外界直接获取
继承:保证代码的高复用性
多态:好看
10、1、2、3、4、5 能组成多少个互不相同且无重复的三位数
a53 5*4*3=60
11、什么是反射?以及应用场景?
可以以字符串的形式去操作对象或者某个模块里的成员
getattr(obj,name,default) 获取对象中的属性或方法,可以设置默认值
setattr(obj,name,value) 给对象的属性赋值,不存在的话先创建
hasattr(obj,name) 判断对象中存在属性和方法与否
delattr(obj,name) 删除对象自身的属性,删除类的属性和方法
注意有时obj不是对象,一般涉及到期本身的属性和内部的方法,而是类本身。
12、metaclass作用?以及应用场景?
先自定义一个元类,然后其子类提通过metaclass=父类名去继承它,可以控制子类的创建过程,需要在父类内部定义一个__new__的方法。
13、用尽量多的方法实现单例模式。
下文已解答
14、装饰器的写法以及应用场景。
用来对用户登陆状态的判断
用来对单例的实现
15、异常处理写法以及如何主动跑出异常(应用场景)
异常处理机制:
程序中的所有异常都会被处理
程序中的所有异常都需要被处理
如果没有处理异常,异常会交给python解释器去执行,直接报错,停止解释器
异常信息的三部分:
异常的追踪位置;即错在何方
异常的内容:哪段代码出错了
异常的信息:是什么错误类型
语法:
try:
# 可能会出错的代码块
except 异常类型 as 别名
#异常处理逻辑
else:
# 没有出错走这
finally:
# 无论错误与否,都会来这
万能异常:Exception BaseException
前者为常规异常的基类,后者为所有异常的基类
16、什么是面向对象的mro
指的是多继承带来的查找顺序的问题,在python3里作为新式类,采用的是深度查询优先,在python2里采用的是经典类,采用的是广度优先。
17、isinstance作用以及应用场景?
isinstance(obj,cls)
检查obj是否是cls的对象
应用场景:取出前端传的值,判断其类型,不满足的话,可以直接返回,避免去数据库查找一次数据。
issubclass(sub,super)
检查sub类是否是supe类r的派生类
18、python的三大特性
补充:
组合 即将一个自定义的对象作为另一个类的属性,也可这样理解,一个类拥有的一个属性正好是另外一个类的对象。
重写:与父类的方法名相同,但是实现体不同,子类调用自己的方法,可以利用super去访问父类的方法 这就成为了重用
super().父类的方法名()
重用;与父类的方法名相同,子类存在自己的逻辑,同时也去执行了父类的方法。
抽离:先写子类,抽离出父类
派生:险些父类,派生出子类
封装、多态、继承
1)、封装的使用:
可以给属性和方法 前加上双下划线 __ 使用时:需要先实例化类,然后通过
obj._cls__func() 或者 obj._cls__name 对象._类__方法或者属性。
封装的目的:
不是 让外界无法访问,而是在完成安全处理后再访问。当然了,对于同行而言 ,封装形同虚设!
约定俗成,对一种属性提供对外的接口时,可以加上
@property 取值 obj.fun 直接调用该函数
@name.setter 赋值 obj.func = 1 直接给该函数传参
@name.delete 删除 del obj.fuc 会调用该函数,若内部存在del self.name 则会真正的去删除
需要注意:property必须在上面,其他随意
2)、继承:子类与父类形成的一种关系,子类可以直接获取父类的对象和属性
优点:减少了代码冗余
实现:将所有子类共同的属性和方法抽离出来,形成父类
继承与封装总结:
父类所有未封装的属性和方法,子类可以直接访问
父类的所有封装的属性和方法,子类同样可以间接访问,还是通过._类名__.....去间接获取
对象的属性,父类无法访问
3) 多态在下面例题中,已做解答
19、接口思想(python里没有接口语法)
接口:建立关联的桥梁,方便管理代码
#清晰知道操作的功能,但是不明确操作的对象
print(len('qwe'))
#清晰知道操作的对象,但是不明确操作的方法
print('qwe'.__len__())
接口类:用来定义功能的类,为继承其的子类提供功能。
一般没有实现体,实现体有继承它的子类自己去实现
class T1:
def test1:
pass
class T2:
def test2:
pass
20、抽象类
抽象父类:拥有抽象方法:(子类共有的方法,但是自身该方法不能有具体的实现体);
抽象方法:有抽象方法的类不能被实例化,会报错
实现:
import abc
class T1(metaclass=abc.ABCMeta):
@abc.abstractmethod
def func(self):pass
class T2(T1):
def func(self):
super().func()
print('这就是继承了抽象父类的方法,自身需要重写该方法')
21、多态
与抽象类息息相关
概念:对象的多种状态--子类对象的多种状态
使用:
import abc
class T1(metaclass=abc.ABCMeta):
def __init__(self,name):
self.name = name
@abs.abstractmethod
def speak(self):pass
class T2(T1):
def speak(self):
print('我的执行体是这样的')
class T3(T1):
def speak(self):
print('我的执行体是另外一种哦')
显而易见:父类的抽象方法对于对应的子类的方法的执行体是多种多样的!
22、鸭子类型
简单来说,类与其他类存在共同的方法名和属性,就是看起来像,就把其当成鸭子类型。
23、格式化方法:
class T4:
def __init__(self,name):
self.name = name
def __str__(self):
return self.name
t = T4()
print(t) # 打印结果为self.name对应的值
# 若没有添加__str__,那么打印的结果为 一个T4类相关的信息
24、析构方法
析构:在对象呗销毁的那一刹那被调用,可以再销毁前做一些事情
class A:
def __init__(self,name):
self.name = name
def __str__(self):
print('打印我哦')
return '1111' # 必须返回一个字符串
def __del__(self):
print('在这个对象被销毁前我走这了')
a = A('michael')
print(a)
# 执行流程,先实例化,走str方法,结束后,执行del方法,最后销毁
25、__setattr__ __getattr__ 的用法
class T6:
def __init__(self,name):
self.name = name
def __setattr__(self, key, value):
self.__dict__[key] = value
# self.key = value RecursionError: maximum recursion depth exceeded
# 即不能通过原始的方式去赋值,否则会陷入死循环
def __getattr__(self, item):
return '找不到该属性的时候会揍我啊'
t = T6('michael')
print(t.age) # '找不到该属性的时候会揍我啊',属性不存在才触发
t.age = 24 # 给属性赋值,触发__setattr__的执行,完成赋值,属性不存在也会触发
print(t.age) # 该属性提供__setattr__完成了赋值,所以存在数据 24
26、getattr,setattr,hashattr的用法
class T7:
def __init__(self,name):
self.name = name
age = 24
def func(self):
return getattr(self,'name','chang')
def func2(self):
setattr(self,'money','14k')
t7 = T7('michael')
print(hasattr(t7, 'name')) # True
# 即hasattr 用来判断对象是否存在该属性,存在为True,不存在为False
print(t7.func()) # michael
# getattr 用来取值,若对象存在该值,返回出来,否则返回设定的默认值 chang
t7.func2() # 触发函数,
print(t7.money) # 14k
# setattr 用来给对象的属性赋值,不存在的话先创建,在赋值,没有返回值
27、自定义异常
class EgonException(BaseException):
def __init__(self,msg):
self.msg = msg
def __str__(self):
return self.msg
try:
raise Egonexception(类型错误)
except EgonException as e:
print(e)
28、断言assert
num = int(input('num:'))
if num > 0:
raise Exception('num为正则,求绝对值没有意义')
print(abs(num))
num = int(input('num:'))
assert num < 0 满足条件才能继续往下走,否则会自动抛出异常
print(abs(num))
29、__call__
class T8:
def __init__(self):
pass
def __call__(self, *args, **kwargs):
print(args, kwargs)
t = T8()
# __call__在对象加括号时触发
t(1,2,3,4,a=5,b=6) #(1, 2, 3, 4) {'a': 5, 'b': 6}
即;想让obj这个对象变为一个可调用的对象,需要 在类中定义__call__
调用__call__的返回值就是__call__的返回值
30、__new__
class Test(object):
def __new__(cls, *args, **kwargs):
print('来到这')
# 必须按这种格式返回创建的实例 ,不然__init__不会触发
return object.__new__(cls, *args, **kwargs)
def __init__(self):
print('再来这')
t = Test() # 来到这
print(t) # 再来这
'''
因此可见:
__new__在创建实例的时候执行,__init__在实例化完成时触发
二者可以不写,默认继承object的
'''
补充:
__init__ __new__ __call__ 三者的混合使用
自己嗨
class T(object):
def __init__(self);
print('到这1')
def __new__(cls,*args,**kwargs):
print('到这2')
return super(T,cls).__new__(cls,*args,**kwargs)
def __call__(self,*args,**kwargs):
print('到这3')
t = T() # 执行 2 1
t() # 执行3
继承其他类:
class Singleon(object):
def __init__(self):
print('来这了1')
def __new__(cls, *args, **kwargs):
print('来到了2')
return super(Singleon, cls).__new__(cls,*args,**kwargs)
def __call__(self, *args, **kwargs):
print('来到这3')
class T(Singleon):
def __init__(self):
print('来到这4')
t = T() # 2 4
t() # 3
31、单例的实现
为什么使用单例:
同一个类实例化多次出来的结果,均指向同一个对象,用于节省内存空间。
有多种实现方式,
1)、基于类方法实现单例
class Mysql:
__instance = None
def __init__(self):
pass
@classmethod
def singleton(cls):
if not cls.__instance:
cls.__instance = cls('127.0.0.1',3306)
return cls.__instance
t1 = Mysql.singleton()
t2 = Mysql.singleton()
print(t1 is t2) #True
2)、基于__new__去实现
class Singleton(object):
def __new__(cls, *args, **kwargs):
if not hasattr(cls,'_instance'):
cls._instance = super(Singleton, cls).__new__(cls,*args,**kwargs)
return cls._instance
class T(Singleton):
def __init__(self):
pass
a = T()
b = T()
print(a is b) # True
3)、基于装饰器去实现
from functools import wraps
def sinleton(cls):
instance = {}
@wraps(cls)
def inner(*args,**kwargs):
if cls not in instance:
instance[cls] = cls(*args,**kwargs)
return instance[cls]
return inner
@singleton # Myclass = singleton(Myclass)
class Myclass(object):
def __init__(object,name):
self.name = name
print(self.name)
obj1 = Myclass('michael')
obj2 = Myclass('tank') # 此时不会打印self.name,因为第二次执行时没有执行Myclass()类。
print(obj1 is obj2) # True
32、元类
不依赖class关键字生成一个类
class_name = 'Michael' # 类名
class_bases = (object,) # 类的基类
class_dic = {} # 类的名称空间
temp = type(class_name,class_bases,class_dic) # 构建有一个class类
print(temp) # <class '__main__.Michael'>
33、eval的用法,exec的用法
eval:
dic_str = "{'a':1,'b':2,'c':3} "
res = eval(dic_str)
print(res,type(res)) #{'a': 1, 'b': 2, 'c': 3} <class 'dict'>
即:eval可以执行字符串里的数据,相当于去除字符串运行。
exec:
g = {'x':1,'y':2} # 作为一个全局作用域,默认为globals()
k = {} # 作为一个局部作用域 ,默认为 locals()
str = '''
global x,z
x = 100
z = 200
m = 300
'''
# str为包含一系列的python代码
exec(str,g,k)
print(g) # {'x': 100, 'y': 2, .....}
# print(k) # {'m': 300}
34、自定义元类控制类的调用过程
class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
def __call__(self, *args, **kwargs):
print(self) # <class '__main__.Test'>
print(args) # ('michael',)
print(kwargs) # {'age': 24}
return 123
class Test(object,metaclass=Mymeta):
def __init__(self,name,age):
print('来到这了')
self.name=name
self.age=age
print(self.name)
print(self.age)
t = Test('michael',age=24)
print(t) # 123
'''
分析:
一切皆对象,因此Test为Mymeta的对象,
在调用Test这个对象类时,会触发元类里的__call__方法,
而不会执行自身的__init__方法。
即__call__会拦截__init__的执行。
'''