正文
Python高级
元类
42.Python中类方法、类实例方法、静态方法有何区别?
类方法: 是类对象的方法,在定义时需要在上方使用 @classmethod 进行装饰,形参为cls,表示类对象,类对象和实例对象都可调用
类实例方法: 是类实例化对象的方法,只有实例对象可以调用,形参为self,指代对象本身;
静态方法: 是一个任意函数,在其上方使用 @staticmethod 进行装饰,可以用对象直接调用,静态方法实际上跟该类没有太大关系
43.遍历一个object的所有属性,并print每一个属性名?
class Car: def __init__(self,name,loss): # loss [价格,油耗,公里数] self.name = name self.loss = loss def getName(self): return self.name def getPrice(self): # 获取汽车价格 return self.loss[0] def getLoss(self): # 获取汽车损耗值 return self.loss[1] * self.loss[2] Bmw = Car("宝马",[60,9,500]) # 实例化一个宝马车对象 print(getattr(Bmw,"name")) # 使用getattr()传入对象名字,属性值。 print(dir(Bmw)) # 获Bmw所有的属性和方法
44.写一个类,并让它尽可能多的支持操作符?
class Array: __list = [] def __init__(self): print "constructor" def __del__(self): print "destruct" def __str__(self): return "this self-defined array class" def __getitem__(self,key): return self.__list[key] def __len__(self): return len(self.__list) def Add(self,value): self.__list.append(value) def Remove(self,index): del self.__list[index] def DisplayItems(self): print "show all items---" for item in self.__list: print item
45.介绍Cython,Pypy Cpython Numba各有什么缺点
Cython
46.请描述抽象类和接口类的区别和联系
1.抽象类: 规定了一系列的方法,并规定了必须由继承类实现的方法。由于有抽象方法的存在,所以抽象类不能实例化。可以将抽象类理解为毛坯房,门窗,墙面的样式由你自己来定,所以抽象类与作为基类的普通类的区别在于约束性更强
2.接口类:与抽象类很相似,表现在接口中定义的方法,必须由引用类实现,但他与抽象类的根本区别在于用途:与不同个体间沟通的规则,你要进宿舍需要有钥匙,这个钥匙就是你与宿舍的接口,你的舍友也有这个接口,所以他也能进入宿舍,你用手机通话,那么手机就是你与他人交流的接口
3.区别和关联:
1.接口是抽象类的变体,接口中所有的方法都是抽象的,而抽象类中可以有非抽象方法,抽象类是声明方法的存在而不去实现它的类
2.接口可以继承,抽象类不行
3.接口定义方法,没有实现的代码,而抽象类可以实现部分方法
4.接口中基本数据类型为static而抽象类不是
47.Python中如何动态获取和设置对象的属性?
if hasattr(Parent, 'x'): print(getattr(Parent, 'x')) setattr(Parent, 'x',3) print(getattr(Parent,'x'))
内存管理与垃圾回收机制
48.哪些操作会导致Python内存溢出,怎么处理?
49.关于Python内存管理,下列说法错误的是 B
A,变量不必事先声明 B,变量无须先创建和赋值而直接使用
C,变量无须指定类型 D,可以使用del释放资源
50.Python的内存管理机制及调优手段?
内存管理机制: 引用计数、垃圾回收、内存池
引用计数:引用计数是一种非常高效的内存管理手段,当一个Python对象被引用时其引用计数增加1,
当其不再被一个变量引用时则计数减1,当引用计数等于0时对象被删除。弱引用不会增加引用计数
垃圾回收:
1.引用计数
引用计数也是一种垃圾收集机制,而且也是一种最直观、最简单的垃圾收集技术。当Python的某个对象的引用计数降为0时,说明没有任何引用指向该对象,该对象就成为要被回收的垃圾了。比如某个新建对象,它被分配给某个引用,对象的引用计数变为1,如果引用被删除,对象的引用计数为0,那么该对象就可以被垃圾回收。不过如果出现循环引用的话,引用计数机制就不再起有效的作用了。
2.标记清除
https://foofish.net/python-gc.html
调优手段
1.手动垃圾回收
2.调高垃圾回收阈值
3.避免循环引用
51.内存泄露是什么?如何避免?
内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费。
有__del__()函数的对象间的循环引用是导致内存泄露的主凶。不使用一个对象时使用: del object 来删除一个对象的引用计数就可以有效防止内存泄露问题。
通过Python扩展模块gc 来查看不能回收的对象的详细信息。
可以通过 sys.getrefcount(obj) 来获取对象的引用计数,并根据返回值是否为0来判断是否内存泄露
函数
52.python常见的列表推导式?
[表达式 for 变量 in 列表] 或者 [表达式 for 变量 in 列表 if 条件]
53.简述read、readline、readlines的区别?
read 读取整个文件
readline 读取下一行
readlines 读取整个文件到一个迭代器以供我们遍历
54.什么是Hash(散列函数)?
散列函数(英语:Hash function)又称散列算法、哈希函数,是一种从任何一种数据中创建小的数字“指纹”的方法。散列函数把消息或数据压缩成摘要,使得数据量变小,将数据的格式固定下来。该函数将数据打乱混合,重新创建一个叫做散列值(hash values,hash codes,hash sums,或hashes)的指纹。散列值通常用一个短的随机字母和数字组成的字符串来代表
55.python函数重载机制?
函数重载主要是为了解决两个问题。 1。可变参数类型。 2。可变参数个数。
另外,一个基本的设计原则是,仅仅当两个函数除了参数类型和参数个数不同以外,其功能是完全相同的,此时才使用函数重载,如果两个函数的功能其实不同,那么不应当使用重载,而应当使用一个名字不同的函数。
好吧,那么对于情况 1 ,函数功能相同,但是参数类型不同,python 如何处理?答案是根本不需要处理,因为 python 可以接受任何类型的参数,如果函数的功能相同,那么不同的参数类型在 python 中很可能是相同的代码,没有必要做成两个不同函数。
那么对于情况 2 ,函数功能相同,但参数个数不同,python 如何处理?大家知道,答案就是缺省参数。对那些缺少的参数设定为缺省参数即可解决问题。因为你假设函数功能相同,那么那些缺少的参数终归是需要用的。
好了,鉴于情况 1 跟 情况 2 都有了解决方案,python 自然就不需要函数重载了。
56.写一个函数找出一个整数数组中,第二大的数
57.手写一个判断时间的装饰器
import datetime class TimeException(Exception): def __init__(self, exception_info): super().__init__() self.info = exception_info def __str__(self): return self.info def timecheck(func): def wrapper(*args, **kwargs): if datetime.datetime.now().year == 2019: func(*args, **kwargs) else: raise TimeException("函数已过时") return wrapper @timecheck def test(name): print("Hello {}, 2019 Happy".format(name)) if __name__ == "__main__": test("backbp")
58.使用Python内置的filter()方法来过滤?
list(filter(lambda x: x % 2 == 0, range(10)))
59.编写函数的4个原则
1.函数设计要尽量短小
2.函数声明要做到合理、简单、易于使用
3.函数参数设计应该考虑向下兼容
4.一个函数只做一件事情,尽量保证函数语句粒度的一致性
60.函数调用参数的传递方式是值传递还是引用传递?
Python的参数传递有:位置参数、默认参数、可变参数、关键字参数。
函数的传值到底是值传递还是引用传递、要分情况:
不可变参数用值传递:像整数和字符串这样的不可变对象,是通过拷贝进行传递的,因为你无论如何都不可能在原处改变不可变对象。
可变参数是引用传递:比如像列表,字典这样的对象是通过引用传递、和C语言里面的用指针传递数组很相似,可变对象能在函数内部改变。
61.如何在function里面设置一个全局变量
globals() # 返回包含当前作用余全局变量的字典。 global 变量 设置使用全局变量
62.对缺省参数的理解 ?
缺省参数指在调用函数的时候没有传入参数的情况下,调用默认的参数,在调用函数的同时赋值时,所传入的参数会替代默认参数。
*args是不定长参数,它可以表示输入参数是不确定的,可以是任意多个。
**kwargs是关键字参数,赋值的时候是以键值对的方式,参数可以是任意多对在定义函数的时候
不确定会有多少参数会传入时,就可以使用两个参数
63.Mysql怎么限制IP访问?
64.带参数的装饰器?
带定长参数的装饰器
def new_func(func): def wrappedfun(username, passwd): if username == 'root' and passwd == '123456789': print('通过认证') print('开始执行附加功能') return func() else: print('用户名或密码错误') return return wrappedfun @new_func def origin(): print('开始执行函数') origin('root','123456789')
带不定长参数的装饰器
def new_func(func): def wrappedfun(*parts): if parts: counts = len(parts) print('本系统包含 ', end='') for part in parts: print(part, ' ',end='') print('等', counts, '部分') return func() else: print('用户名或密码错误') return func() return wrappedfun
65.为什么函数名字可以当做参数用?
Python中一切皆对象,函数名是函数在内存中的空间,也是一个对象
66.Python中pass语句的作用是什么?
在编写代码时只写框架思路,具体实现还未编写就可以用pass进行占位,是程序不报错,不会进行任何操作。
67.有这样一段代码,print c会输出什么,为什么?
a = 10 b = 20 c = [a] a = 15
答:10对于字符串,数字,传递是相应的值
68.交换两个变量的值?
a, b = b, a
69.map函数和reduce函数?
map(lambda x: x * x, [1, 2, 3, 4]) # 使用 lambda # [1, 4, 9, 16] reduce(lambda x, y: x * y, [1, 2, 3, 4]) # 相当于 ((1 * 2) * 3) * 4 # 24
70.回调函数,如何通信的?
回调函数是把函数的指针(地址)作为参数传递给另一个函数,将整个函数当作一个对象,赋值给调用的函数。
71.Python主要的内置数据类型都有哪些? print dir( ‘a ’) 的输出?
内建类型:布尔类型,数字,字符串,列表,元组,字典,集合
输出字符串'a'的内建方法
72.map(lambda x:xx,[y for y in range(3)])的输出?
[0, 1, 4]
73.hasattr() getattr() setattr() 函数使用详解?
hasattr(object,name)函数:
判断一个对象里面是否有name属性或者name方法,返回bool值,有name属性(方法)返回True,否则返回False。
class function_demo(object): name = 'demo' def run(self): return "hello function" functiondemo = function_demo() res = hasattr(functiondemo, "name") # 判断对象是否有name属性,True res = hasattr(functiondemo, "run") # 判断对象是否有run方法,True res = hasattr(functiondemo, "age") # 判断对象是否有age属性,False print(res)
getattr(object, name[,default])函数:
获取对象object的属性或者方法,如果存在则打印出来,如果不存在,打印默认值,默认值可选。注意:如果返回的是对象的方法,则打印结果是:方法的内存地址,如果需要运行这个方法,可以在后面添加括号().
functiondemo = function_demo() getattr(functiondemo, "name")# 获取name属性,存在就打印出来 --- demo getattr(functiondemo, "run") # 获取run 方法,存在打印出方法的内存地址 getattr(functiondemo, "age") # 获取不存在的属性,报错 getattr(functiondemo, "age", 18)# 获取不存在的属性,返回一个默认值
setattr(object, name, values)函数:
给对象的属性赋值,若属性不存在,先创建再赋值
class function_demo(object): name = "demo" def run(self): return "hello function" functiondemo = function_demo() res = hasattr(functiondemo, "age") # 判断age属性是否存在,False print(res) setattr(functiondemo, "age", 18) # 对age属性进行赋值,无返回值 res1 = hasattr(functiondemo, "age") # 再次判断属性是否存在,True
综合使用
class function_demo(object): name = "demo" def run(self): return "hello function" functiondemo = function_demo() res = hasattr(functiondemo, "addr") # 先判断是否存在 if res: addr = getattr(functiondemo, "addr") print(addr) else: addr = getattr(functiondemo, "addr", setattr(functiondemo, "addr", "北京首都")) print(addr)
74.一句话解决阶乘函数?
reduce(lambda x,y : x*y,range(1,n+1))
75.什么是lambda函数? 有什么好处?
lambda 函数是一个可以接收任意多个参数(包括可选参数)并且返回单个表达式值的函数
1.lambda函数比较轻便,即用即仍,很适合需要完成一项功能,但是此功能只在此一处使用,连名字都很随意的情况下
2.匿名函数,一般用来给filter,map这样的函数式编程服务
3.作为回调函数,传递给某些应用,比如消息处理
76.递归函数停止的条件?
递归的终止条件一般定义在递归函数内部,在递归调用前要做一个条件判断,根据判断的结果选择是继续调用自身,还是return,,返回终止递归。
终止的条件:判断递归的次数是否达到某一限定值
2.判断运算的结果是否达到某个范围等,根据设计的目的来选择
77.下面这段代码的输出结果将是什么?请解释。
def multipliers(): return [lambda x: i *x for i in range(4)] print([m(2) for m in multipliers()])
上面代码的输出结果是[6,6,6,6],不是我们想的[0,2,4,6]
你如何修改上面的multipliers的定义产生想要的结果?
上述问题产生的原因是python闭包的延迟绑定。这意味着内部函数被调用时,参数的值在闭包内进行查找。因此,当任何由multipliers()返回的函数被调用时,i的值将在附近的范围进行查找。那时,不管返回的函数是否被调用,for循环已经完成,i被赋予了最终的值3.
def multipliers(): for i in range(4): yield lambda x: i *x
def multipliers(): return [lambda x,i = i: i*x for i in range(4)]
78.什么是lambda函数?它有什么好处?写一个匿名函数求两个数的和
lambda函数是匿名函数,使用lambda函数能创建小型匿名函数,这种函数得名于省略了用def声明函数的标准步骤
设计模式
79.对设计模式的理解,简述你了解的设计模式?
设计模式是经过总结,优化的,对我们经常会碰到的一些编程问题的可重用解决方案。一个设计模式并不像一个类或一个库那样能够直接作用于我们的代码,反之,设计模式更为高级,它是一种必须在特定情形下实现的一种方法模板。 常见的是工厂模式和单例模式
80.请手写一个单例
#python2 class A(object): __instance = None def __new__(cls,*args,**kwargs): if cls.__instance is None: cls.__instance = objecet.__new__(cls) return cls.__instance else: return cls.__instance
81.单例模式的应用场景有那些?
单例模式应用的场景一般发现在以下条件下: 资源共享的情况下,避免由于资源操作时导致的性能或损耗等,如日志文件,应用配置。 控制资源的情况下,方便资源之间的互相通信。如线程池等,1,网站的计数器 2,应用配置 3.多线程池 4数据库配置 数据库连接池 5.应用程序的日志应用...
82.用一行代码生成[1,4,9,16,25,36,49,64,81,100]
print([x*x for x in range(1, 11)])
83.对装饰器的理解,并写出一个计时器记录方法执行性能的装饰器?
装饰器本质上是一个callable object ,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。
import time from functools import wraps def timeit(func): @wraps(func) def wrapper(*args, **kwargs): start = time.clock() ret = func(*args, **kwargs) end = time.clock() print('used:',end-start) return ret return wrapper @timeit def foo(): print('in foo()'foo())
84.解释以下什么是闭包?
在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包。
85.函数装饰器有什么作用?
装饰器本质上是一个callable object,它可以在让其他函数在不需要做任何代码的变动的前提下增加额外的功能。装饰器的返回值也是一个函数的对象,它经常用于有切面需求的场景。比如:插入日志,性能测试,事务处理,缓存。权限的校验等场景,有了装饰器就可以抽离出大量的与函数功能本身无关的雷同代码并发并继续使用。 详细参考:https://manjusaka.itscoder.com/2018/02/23/something-about-decorator/
86.生成器,迭代器的区别?
迭代器是遵循迭代协议的对象。用户可以使用 iter() 以从任何序列得到迭代器(如 list, tuple, dictionary, set 等)。另一个方法则是创建一个另一种形式的迭代器 —— generator 。要获取下一个元素,则使用成员函数 next()(Python 2)或函数 next() function (Python 3) 。当没有元素时,则引发 StopIteration 此例外。若要实现自己的迭代器,则只要实现 next()(Python 2)或 __next__()( Python 3)
生成器(Generator),只是在需要返回数据的时候使用yield语句。每次next()被调用时,生成器会返回它脱离的位置(它记忆语句最后一次执行的位置和所有的数据值)
区别: 生成器能做到迭代器能做的所有事,而且因为自动创建iter()和next()方法,生成器显得特别简洁,而且生成器也是高效的,使用生成器表达式取代列表解析可以同时节省内存。除了创建和保存程序状态的自动方法,当发生器终结时,还会自动抛出StopIteration异常。
官方介绍:https://docs.python.org/3/tutorial/classes.html#iterators
87.X是什么类型?
X= (i for i in range(10)) X是 generator类型
88.请用一行代码 实现将1-N 的整数列表以3为单位分组
N =100 print ([[x for x in range(1,100)] [i:i+3] for i in range(0,100,3)])
89.Python中yield的用法?
yield就是保存当前程序执行状态。你用for循环的时候,每次取一个元素的时候就会计算一次。用yield的函数叫generator,和iterator一样,它的好处是不用一次计算所有元素,而是用一次算一次,可以节省很多空间,generator每次计算需要上一次计算结果,所以用yield,否则一return,上次计算结果就没了
面向对象
90.Python中的可变对象和不可变对象?
不可变对象,该对象所指向的内存中的值不能被改变。当改变某个变量时候,由于其所指的值不能被改变,相当于把原来的值复制一份后再改变,这会开辟一个新的地址,变量再指向这个新的地址。
可变对象,该对象所指向的内存中的值可以被改变。变量(准确的说是引用)改变后,实际上其所指的值直接发生改变,并没有发生复制行为,也没有开辟出新的地址,通俗点说就是原地改变。
Pyhton中,数值类型(int 和float),字符串str、元祖tuple都是不可变类型。而列表list、字典dict、集合set是可变类型
91.Python的魔法方法
魔法方法就是可以给你的类增加魔力的特殊方法,如果你的对象实现(重载)了这些方法中的某一个,那么这个方法就会在特殊的情况下被Python所调用,你可以定义自己想要的行为,而这一切都是自动发生的,它们经常是两个下划线包围来命名的(比如__init___,__len__),Python的魔法方法是非常强大的所以了解其使用方法也变得尤为重要!
__init__构造器,当一个实例被创建的时候初始化的方法,但是它并不是实例化调用的第一个方法。
__new__才是实例化对象调用的第一个方法,它只取下cls参数,并把其他参数传给__init___.
___new__很少使用,但是也有它适合的场景,尤其是当类继承自一个像元祖或者字符串这样不经常改变的类型的时候。
__call__让一个类的实例像函数一样被调用
__getitem__定义获取容器中指定元素的行为,相当于self[key]
__getattr__定义当用户试图访问一个不存在属性的时候的行为。
__setattr__定义当一个属性被设置的时候的行为
__getattribute___定义当一个属性被访问的时候的行为
92.面向对象中怎么实现只读属性?
将对象私有化,通过共有方法提供一个读取数据的接口
class person: def __init__(self, x): self.__age = 10 def age(self): return self.__age t = person(22) # t.__age =100 print(t.age())
最好的方法
class MyCls(object): __weight = 50 @property def weight(self): return self.__weight
93.谈谈你对面向对象的理解?
面向对象是相当于面向过程而言的,面向过程语言是一种基于功能分析的,以算法为中心的程序设计方法,而面向对象是一种基于结构分析的,以数据为中心的程序设计思想。在面向对象语言中有一个很重要的东西,叫做类。面向对象有三大特性:封装、继承、多态。