python题目
1、python的函数参数传递
#-*- coding:utf-8 -*- """ 两个函数表现不同的原因是: 1、所有的变量都可以理解为对内存中对象的“引用”,而类型是属于对象的,而不是变量的; 2、在是否可以修改的问题上,分为两种对象,一种是“可修改”的对象,一种是“不可修改”的对象; 其中,string、tuple、number是不可修改的对象;而list、dict、set是可修改的对象 3、当引用传递给函数的时候,函数自动复制了这一份引用;比如,函数fun复制了一份a的引用,指向的是对象数字1, 此时对象1有两个引用; 执行a = 2的运算时,因为number是不可变类型,函数fun将这个引用又指向了新的对象 数字2; 此时,对象1有一个引用;对象2有一个引用; 而函数fun2复制了一份b的引用,指向的是对象 一个空列表,此时这个空列表有两个引用; 执行b.append(1)的运算时,因为list是可变类型,函数fun2直接修改了这个列表; 此时,对象 list依然是两个引用; """ a = 1 print id(a) def fun(a): print a print "a: "+str(id(a)) a = 2 print "a = 2中a: "+ str(id(a))+", 2: "+ str(id(2)) return a print fun(a) print a b = [] print id(b) def fun2(b): print b print id(b) b.append(1) print "b: "+ str(id(b)) return b print fun2(b) print b
2、python的元类
#-*- coding:utf-8 -*- """ 精彩文章链接:http://python.jobbole.com/88795/ 元类:元类也就是类的类,类实例化是实例对象,元类实例化是类; """
3、生成器和迭代器
#-*- coding:utf-8 -*- """ 精彩文章:http://python.jobbole.com/87805/ 问题:将列表生成式中的[]换成(),数据结构是否有变化? 答:有变化,从列表变成了生成器;也就是这是一个生成器表达式; 可迭代对象:支持__iter__()方法,一个可迭代对象不一定是迭代器; 迭代器:任何实现了__iter__()和__next__()(python2中实现next())方法的对象都是迭代器,__iter__() 返回迭代器自身,__next__()返回容器中的下一个值,如果容器中没有更多元素了,则抛出StopIteration异常生成器也是迭代器; 迭代器不会一次性把所有元素加载到内存,而是需要的时候才生成返回结果 迭代器就是实现了工厂模式的对象,它在你每次你询问要下一个值的时候给你返回。 有很多关于迭代器的例子,比如itertools函数返回的都是迭代器对象; 生成器:生成器是一种特殊的迭代器;使用yield关键字返回结果; 生成器和普通函数的区别是使用yield,而不是return返回值; yield每次返回一个结果,在每个结果的中间会挂起函数,便于下次从离开的地方继续; yield item这行代码会产出一个值,提供给next的调用方,然后,暂停执行生成器,也就是挂起函数, 直到下一次调用next(); """ L = [1,2,3] l = iter(L) # <listiterator object at 0x029A6370> 这个就是将一个可迭代对象L变成迭代器l L1 = list(l) # [1, 2, 3] 迭代器可以用list转换成一个列表 list1 = [x*x for x in range(1,6)] print type(list1) # <type 'list'> print list1 #[1, 4, 9, 16, 25] # 生成器表达式 g = (x*x for x in range(1,2)) print type(g) # <type 'generator'> print g.next() # 1 # 以下定义个一个生成器,通常用yield关键字; def createGenerator(): mylist = range(3) for i in mylist: yield i*i # 创建生成器,当调用这个函数的时候,函数的代码并没有运行,仅仅返回的是生成器对象 mygenerator = createGenerator() for i in mygenerator: print i #用生成器实现斐波那契数列(前面两个数相加等于后面那个的数,eg:[1,1,2,3,5,8,13,21,34,55.....]) def fib(): prev,curl = 0,1 while True: yield curl prev,curl = curl,prev+curl import itertools f = fib() print list(itertools.islice(f,0,10))
4、单例模式
#-*- coding:utf-8 -*- """ 精彩博客讲解:http://python.jobbole.com/87294/ 单例模式是一种常见的软件设计模式,该设计的主要目的是确保某一个类只有只有一个实例存在; 在python中,可以有多种方法来实现单例模式: 1、使用模块 2、使用__new__ 3、使用装饰器 4、使用元类 使用模块:python的模块本身就是天然的单例模式,因为模块在第一次导入是会生成.pyc文件,第二次导入时会直接 加载.pyc文件,而不会再次执行模块代码; """ # 使用模块 import # mysingleton.py class My_Singleton(object): def foo(self): pass my_singleton = My_Singleton() # to use from mysingleton import my_singleton my_singleton.foo() # 使用__new__()方法 class Singleton1(object): def __new__(cls,*args,**kwargs): #print "12" if not hasattr(cls,'_instance'): cls._instance = super(Singleton1,cls).__new__(cls,*args,**kwargs) # 创建实例 return cls._instance class Myclass1(Singleton1): # 理解为方法的继承,对__new__方法也是同样的继承,没有重写,就是使用基类的方法; a = 1 m1 = Myclass1() m2 = Myclass1() print m1 is m2 # 返回True,为同一个实例 # 使用装饰器 def singleton(cls): # 装饰器的参数是类。 instances = {} def getinstance(*args, **kw): if cls not in instances: instances[cls] = cls(*args, **kw) # 创建实例 return instances[cls] return getinstance @singleton class MyClass: a = 2 mm1 = MyClass() mm2 = MyClass() print mm1 is mm2 # True
5、引用和深、浅拷贝
# -*- coding:utf-8 -*- """ 引用:python中变量的赋值都是进行对象的引用(内存地址)传递; 浅拷贝:copy.copy()可以进行对象的浅拷贝,它复制了对象,但对于对象中的元素依然使用原始的引用; 深拷贝:copy.deepcopy()复制了对象已经所有元素; """ import copy a = [1, 2, 3, 4,['a','b']] #原始对象 b = a #赋值,传对象的引用 c = copy.copy(a) #对象拷贝,浅拷贝 d = copy.deepcopy(a) #对象拷贝,深拷贝 a.append(5) #修改对象a a[4].append('c') #修改对象a中的['a', 'b']数组对象 print 'a = ', a print id(a) print 'b = ', b print id(b) print 'c = ', c print id(c) print 'd = ', d print id(d)
6、python垃圾回收机制
# -*- coding:utf-8 -*- """ python的垃圾回收机制 1、引用计数 Pyobject是结构体,存储所有对象共同的数据成员,其中ob_refcnt用做引用计数,当一个对象有新的引用时,它的 ob_refcnt就会增加,当引用它的对象被删除,它的ob_refcnt就会减少,当引用计数为0时,该对象生命就结束了; 2、标记-清除 标记-清除解决了循环引用的问题; 基本思路:? 3、分代回收 思路是:将系统中的所有内存块根据其存活时间划分为不同的集合,每个集合就成为一个“代”,垃圾棘手频率 随着“代”的存活时间的增大而减小,存活时间通常利用经过几次垃圾回收来度量; python默认定义了三代对象集合,索引数越大对象存活时间越长; """
7、python的is和==
# -*- coding:utf-8 -*- """ is是比对地址,也就是对象是不是同一个 == 是比对值,比对对象的值相不相等 """
8、read、readline和readlines区别
# -*- coding:utf-8 -*- """ read 读取整个文件,返回的是str对象 readline 读取下一行,使用生成器方法,返回的是str对象 readlines 读取整个文件,返回的是文件行的列表 当没有足够内存可以一次读取整个文件时,才应该使用readline() 当内存充足时,readlines()的效率更高 """
9、range和xrange的区别
# -*- coding:utf-8 -*- """ range()和xrange() 都在循环时使用,xrange内存性能更好; 因为range生成的是列表,保存在内存中,占用内存资源; 而xrange生成的是生成器,每次调用返回一个值,不需要上来就占用一块很大的内存空间; """
10、经典类和新式类的区别
# -*- coding:utf-8 -*- """ 经典类 在类多重继承的时候,采用的是从左到右边的深度优先的原则匹配方法; 新式类 采用的广度优先 以下例子: 按照经典类的查找顺序从左到右深度优先的规则,在访问d.foo1()的时候,D这个类是没有的.. 那么往上查找,先找到B,里面没有,深度优先,访问A,找到了foo1(), 所以这时候调用的是A的foo1(),从而导致C重写的foo1()被绕过 """ class A(): def foo1(self): print "A" class B(A): def foo2(self): pass class C(A): def foo1(self): print "C" class D(B, C): pass d = D() d.foo1()
11、lambda表达式
# -*- coding:utf-8 -*- """ lambda表达式,也就是匿名函数,本质上是一个函数,只能由一条表达式组成; 使用场景:当需要一个函数,这个函数只执行一次,可以使用匿名函数; 可以少定义一个函数,并且能清晰的看到做了什么处理; """ f = map(lambda x:x*2,[1,2,3,4]) # 利用lambda表达式 print f def fun(n): return n*2 f2 = map(fun,[1,2,3,4]) print f2
12、python函数式编程
# -*- coding:utf-8 -*- """ 函数式编程:理念是把函数当做参数来用,关注的描述问题而不是如何实现; 精彩博文:https://coolshell.cn/articles/10822.html python中函数式编程支持:map()函数、reduce()函数、filter()函数 三个高阶函数 map():一个入参,对一个序列的每个项一次执行函数; reduce():两个入参,对一个序列的每个项迭代调用函数; filter():一个入参,相当于一个过滤器,调用一个布尔函数bool_func来遍历每个seq中的元素, 返回一个使bool_func返回true的元素的序列; """ a = [1,2,3,4,5,6,7] f1 = map(lambda x:x+1,a) f2 = reduce(lambda x,y:x*y,range(1,5)) # 求4的阶乘 f3 = filter(lambda x:x%2==0,a) # 求得偶数 print f1 print f2 print f3
13、*args和**kwargs
# -*- coding:utf-8 -*- """ 函数式编程:理念是把函数当做参数来用,关注的描述问题而不是如何实现; 精彩博文:https://coolshell.cn/articles/10822.html python中函数式编程支持:map()函数、reduce()函数、filter()函数 三个高阶函数 map():一个入参,对一个序列的每个项一次执行函数; reduce():两个入参,对一个序列的每个项迭代调用函数; filter():一个入参,相当于一个过滤器,调用一个布尔函数bool_func来遍历每个seq中的元素, 返回一个使bool_func返回true的元素的序列; """ a = [1,2,3,4,5,6,7] f1 = map(lambda x:x+1,a) f2 = reduce(lambda x,y:x*y,range(1,5)) # 求4的阶乘 f3 = filter(lambda x:x%2==0,a) # 求得偶数 print f1 print f2 print f3
14、装饰器
# -*- coding:utf-8 -*- """ 装饰器是一种设计模式: 装饰器:装饰器运用闭包的基本原理,对一个目标函数进行装饰,为已经存在的对象添加额外的功能; 应用场景:被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。装饰器是解决这类问题的 绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用 """ # 通用的装饰器 例子 def decorator(func): def inner(*args,**kwargs): print "装饰器函数中。。目标函数执行之前的操作。" res = func(*args,**kwargs) print "装饰器函数中。。目标函数执行之后的操作。" return res return inner @decorator def destination(a): print "目标函数接收到参数 %s" % a return "目标函数的返回值%s"%a # 多层装饰器 def decorator_out(func): def inner(*args,**kwargs): print "外层装饰器函数中。。目标函数执行之前的操作。" res = func(*args,**kwargs) print "外层装饰器函数中。。目标函数执行之后的操作。" return res return inner def decorator_in(func): def inner(*args,**kwargs): print "内层装饰器函数中。。目标函数执行之前的操作。" res = func(*args,**kwargs) print "内层装饰器函数中。。目标函数执行之后的操作。" return res return inner @decorator_out @decorator_in def destination2(a): print "目标函数接收到参数 %s" % a return "目标函数的返回值%s"%a # 带参数的装饰器 def decorator_val(flag): def decorator(func): def inner(*args,**kwargs): print "装饰器函数中。。目标函数执行之前的操作。" res = func(*args,**kwargs) if flag: print "装饰器函数中。。目标函数执行之后的操作。" return res return inner return decorator @decorator_val(False) def destination3(a): print "目标函数接收到参数 %s" % a return "目标函数的返回值%s"%a # python内置装饰器 """ Python内置装饰器 在Python中有三个内置的装饰器,都是跟class相关的:staticmethod、classmethod 和property。 staticmethod 是类静态方法,其跟成员方法的区别是没有 self 参数,并且可以在类不进行实例化的情况下调用 classmethod 与成员方法的区别在于所接收的第一个参数不是 self (类实例的指针),而是cls(当前类的具体类型) property 是属性的意思,表示可以通过类实例直接访问的信息 """ if __name__ == "__main__": res = destination3(10) print res
15、常用设计模式
# -*- coding:utf-8 -*- """ 常用设计模式: 工厂模式 单例模式 装饰器 """
16、GIL线程全局锁
# -*- coding:utf-8 -*- """ GIL线程全局锁:是python为了线程安全而采取的独立线程运行的限制,说白了就是一个核在同一时间只能运行一个线程; 同一时间片就只能由一个线程获得GIL全局锁,而另一个占用CPU的线程则无法执行,继续等待cpu时间就白白浪费掉, 也就是只有获得GIL锁的线程才能真正在cpu上运行,所以,多线程在python中只能交替执行,即使100个线程跑在100核cpu上, 也只能用到一个核。 既然python的多线程在多核主机上这么鸡肋,那么有什么更好的方式实现并发吗? 用 多进程+协程 代替 多线程 的方式; 在多进程中,由于每个进程都是独立的存在,所以每个进程间的线程都拥有独立的GIL锁,互不影响。 但是,由于进程之间的独立的存在,所以进程间通信就需要通过队列的方式来实现。 多进程实现? 以及协程的概念? """
17、协程
# -*- coding:utf-8 -*- """ 什么是协程呢? 协程又称为微线程,协程只有一个线程执行,由程序自身控制同一线程中不同子程序的切换; 协程在定义体中包含yield关键字,由yield控制流程; 比如,在执行函数A时,可以随时中断,去执行函数B,然后中断继续执行函数A,这之间的切换都是由程序自身控制的; 优势: 1、执行效率高,因为子程序切换(函数)不是线程切换,由程序自身控制,没有切换线程的开销; 2、因为只有一个线程,不需要多线程的锁机制; 协程可以处理IO密集型程序的效率问题,但是处理CPU密集型不是它的长处, 可以采用 多进程+协程 的方式 充分发挥CPU利用率; 来看例子: 传统的生产者-消费者模型是一个线程写消息,一个线程取消息,通过锁机制控制队列和等待, 但一不小心就可能死锁。 如果改用协程,生产者生产消息后,直接通过yield跳转到消费者开始执行,待消费者执行完毕后, 切换回生产者继续生产,效率极高: """ def consumer(): r = '' while True: m = yield r print("[CONSUMER] Consuming %s..." % m) r = '200 ok' def product(c): next(c) # 最先调用 next(c) 函数 这一步通常称为 “预激”(prime)协程 # (即,让协程向前执行到第一个 yield 表达式,准备好作为活跃的协程使用) # 调用c.send(None),效果一样 n = 0 while n < 5: n = n + 1 print("[PRODUCT] Producting %s..." % n) r = c.send(n) # send 方法 的参数 会成为暂停的 yield 表达式的值,在此处赋值给m print("[PRODUCT] Consumer return %s..." % r) c.close() if __name__ == "__main__": c = consumer() product(c)
18、闭包
# -*- coding:utf-8 -*- """ 精彩博文:https://www.cnblogs.com/Lin-Yi/p/7305364.html 闭包:指延伸了作用域的函数,其中包含函数定义体中引用,但是不在定义体中定义的非全局变量, 它能访问定义体之外定义的非全局变量; 通俗的讲,在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数 的返回值是内函数的引用,这样就构成了一个闭包。 一般情况下,在我们的认知中,如果一个函数结束,函数内部所有东西都会释放掉,局部变量都会消失, 但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到, 就把这个临时变量绑定给了内部函数,然后自己再结束。 闭包的用途: 1、装饰器; 2、实现单例模式,其实这也是装饰器的应用; 闭包中的内函数修改外函数局部变量: 1、在python3中,可以用nonlocal关键字声明一个变量,表示指责格变量不是局部变量空间的变量,需要向上一层 变量空间中查找这个变量; 2、在python2中,没有nonlocal这个关键字,我们可以把闭包变量改成可变类型数据进行修改,比如列表; """ def maker(n): # 其中函数maker()又可以成为工厂函数,f和g都是它生产的,f和g是不同的函数对象 def action(m): return n*m return action def outer(a): b = 10 # a和b都是闭包变量 c = [a] def inner(): # nonlocal b # b += 1 c[0] += 1 print c[0] # print b return inner if __name__ == "__main__": f = maker(6) print f(1) g = maker(10) print g(9) demo = outer(5) demo() # 输出6
19、python作用域
# -*- coding:utf-8 -*- """ Python 中,一个变量的作用域总是由在代码中被赋值的地方所决定的。 当 Python 遇到一个变量的话他会按照这样的顺序进行搜索: 本地作用域(Local)→当前作用域被嵌入的本地作用域(Enclosing locals) →全局/模块作用域(Global)→内置作用域(Built-in) 作用域: 系统变量 全局变量 嵌套作用域 本地作用域 """
20、__new__和__init__的区别
# -*- coding:utf-8 -*- """ 精彩博文;https://baijiahao.baidu.com/s?id=1609386059712096379&wfr=spider&for=pc 1、__new__是一个类方法,而__init__是一个实例方法; 2、__new__方法会返回一个创建的实例,而__init__什么都不返回; 3、只有在__new__返回一个cls的实例时,后面的__init__才能被调用;两者的参数是一致的; 4、当创建一个新实例时调用__new__,初始化一个实例调用__init__; __metaclass__是创建类时起作用.所以我们可以分别使用__metaclass__,__new__和__init__来分别 在类创建,实例创建和实例初始化的时候做一些小手脚; """ class Person(object): def __new__(cls, name,age): print "__new__" return super(Person,cls).__new__(cls,name,age) def __init__(self,name,age): print "__init__" self._name = name self._age = age def __str__(self): return "person name is %s, age is %d" %(self._name,self._age) p = Person("hahh",22) print p """ 具体的执行逻辑是这样的: 1. p = Person(name, age) 2. 首先执行使用name和age参数来执行Person类的__new__方法,这个__new__方法会 返回Person类的一个实例 (通常情况下是使用 super(Persion, cls).__new__(cls, ... ...) 这样的方式), 3. 然后利用这个实例来调用类的__init__方法,上一步里面__new__产生的实例也就是 __init__里面的的 self 所以,__init__ 和 __new__ 最主要的区别在于: 1.__init__ 通常用于初始化一个新实例,控制这个初始化的过程,比如添加一些属性, 做一些额外的操作, 发生在类实例被创建完以后。它是实例级别的方法。 2.__new__ 通常用于控制生成一个新实例的过程。它是类级别的方法。 """
21、super init
# -*- coding:utf-8 -*- """ super可以不用显示的声明基类; """