通过id来看引用a的内存地址可以比较理解:
a=1deffun(a):print"func_in",id(a)#func_in41322472a=2print"re-point",id(a),id(2)#re-point4132244841322448print"func_out",id(a),id(1)#func_out4132247241322472fun(a)printa#1注:具体的值在不同电脑上运行时可能不同。
可以看到,在执行完a=2之后,a引用中保存的值,即内存地址发生变化,由原来1对象的所在的地址变成了2这个实体对象的内存地址。
而第2个例子a引用保存的内存值就不会发生变化:
a=[]deffun(a):print"func_in",id(a)#func_in53629256a.append(1)print"func_out",id(a)#func_out53629256fun(a)printa#[1]这里记住的是类型是属于对象的,而不是变量。而对象有两种,“可更改”(mutable)与“不可更改”(immutable)对象。在python中,strings,tuples,和numbers是不可更改的对象,而list,dict,set等则是可以修改的对象。(这就是这个问题的重点)
当一个引用传递给函数的时候,函数自动复制一份引用,这个函数里的引用和外边的引用没有半毛关系了.所以第一个例子里函数把引用指向了一个不可变对象,当函数返回的时候,外面的引用没半毛感觉.而第二个例子就不一样了,函数内的引用指向的是可变对象,对它的操作就和定位了指针地址一样,在内存里进行修改.
Python其实有3个方法,即静态方法(staticmethod),类方法(classmethod)和实例方法,如下:
deffoo(x):print"executingfoo(%s)"%(x)classA(object):deffoo(self,x):print"executingfoo(%s,%s)"%(self,x)@classmethoddefclass_foo(cls,x):print"executingclass_foo(%s,%s)"%(cls,x)@staticmethoddefstatic_foo(x):print"executingstatic_foo(%s)"%xa=A()这里先理解下函数参数里面的self和cls.这个self和cls是对类或者实例的绑定,对于一般的函数来说我们可以这么调用foo(x),这个函数就是最常用的,它的工作跟任何东西(类,实例)无关.对于实例方法,我们知道在类里每次定义方法的时候都需要绑定这个实例,就是foo(self,x),为什么要这么做呢因为实例方法的调用离不开实例,我们需要把实例自己传给函数,调用的时候是这样的a.foo(x)(其实是foo(a,x)).类方法一样,只不过它传递的是类而不是实例,A.class_foo(x).注意这里的self和cls可以替换别的参数,但是python的约定是这俩,还是不要改的好.
对于静态方法其实和普通的方法一样,不需要对谁进行绑定,唯一的区别是调用的时候需要使用a.static_foo(x)或者A.static_foo(x)来调用.
更多关于这个问题:
类变量:
是可在类的所有实例之间共享的值(也就是说,它们不是单独分配给每个实例的)。例如下例中,num_of_instance就是类变量,用于跟踪存在着多少个Test的实例。
实例变量:
实例化之后,每个实例单独拥有的变量。
classTest(object):num_of_instance=0def__init__(self,name):self.name=nameTest.num_of_instance+=1if__name__=='__main__':printTest.num_of_instance#0t1=Test('jack')printTest.num_of_instance#1t2=Test('lucy')printt1.name,t1.num_of_instance#jack2printt2.name,t2.num_of_instance#lucy2补充的例子
classPerson:name="aaa"p1=Person()p2=Person()p1.name="bbb"printp1.name#bbbprintp2.name#aaaprintPerson.name#aaa这里p1.name="bbb"是实例调用了类变量,这其实和上面第一个问题一样,就是函数传参的问题,p1.name一开始是指向的类变量name="aaa",但是在实例的作用域里把类变量的引用改变了,就变成了一个实例变量,self.name不再引用Person的类变量name了.
可以看看下面的例子:
这个也是python彪悍的特性.
自省就是面向对象的语言所写的程序在运行时,所能知道对象的类型.简单一句就是运行时能够获得对象的类型.比如type(),dir(),getattr(),hasattr(),isinstance().
a=[1,2,3]b={'a':1,'b':2,'c':3}c=Trueprinttype(a),type(b),type(c)#
d={key:valuefor(key,value)initerable}7Python中单下划线和双下划线>>>classMyClass():...def__init__(self):...self.__superprivate="Hello"...self._semiprivate=",world!"...>>>mc=MyClass()>>>printmc.__superprivateTraceback(mostrecentcalllast):File"
_foo:一种约定,用来指定变量私有.程序员用来指定私有变量的一种方式.不能用frommoduleimport*导入,其他方面和公有一样访问;
__foo:这个有真正的意义:解析器用_classname__foo来代替这个名字,以区别和其他类相同的命名,它无法直接像公有成员一样随便访问,通过对象名._类名__xxx这样的方式可以访问.
.format在许多方面看起来更便利.对于%最烦人的是它无法同时传递一个变量和元组.你可能会想下面的代码不会有什么问题:
"hithere%s"%name但是,如果name恰好是(1,2,3),它将会抛出一个TypeError异常.为了保证它总是正确的,你必须这样做:
"hithere%s"%(name,)#提供一个单元素的数组而不是一个参数但是有点丑…format就没有这些问题.你给的第二个问题也是这样,.format好看多了.
你为什么不用它
这里有个关于生成器的创建问题面试官有考:问:将列表生成式中[]改成()之后数据结构是否改变?答案:是,从列表变为生成器
>>>L=[x*xforxinrange(10)]>>>L[0,1,4,9,16,25,36,49,64,81]>>>g=(x*xforxinrange(10))>>>g
用*args和**kwargs只是为了方便并没有强制使用它们.
当你不确定你的函数里将要传递多少参数时你可以用*args.例如,它可以传递任意数量的参数:
>>>defprint_everything(*args):forcount,thinginenumerate(args):...print'{0}.{1}'.format(count,thing)...>>>print_everything('apple','banana','cabbage')0.apple1.banana2.cabbage相似的,**kwargs允许你使用没有事先定义的参数名:
>>>deftable_things(**kwargs):...forname,valueinkwargs.items():...print'{0}={1}'.format(name,value)...>>>table_things(apple='fruit',cabbage='vegetable')cabbage=vegetableapple=fruit你也可以混着用.命名参数首先获得参数值然后所有的其他参数都传递给*args和**kwargs.命名参数在列表的最前端.例如:
deftable_things(titlestring,**kwargs)*args和**kwargs可以同时在函数的定义中,但是*args必须在**kwargs前面.
当调用函数时你也可以用*和**语法.例如:
>>>defprint_three_things(a,b,c):...print'a={0},b={1},c={2}'.format(a,b,c)...>>>mylist=['aardvark','baboon','cat']>>>print_three_things(*mylist)a=aardvark,b=baboon,c=cat就像你看到的一样,它可以传递列表(或者元组)的每一项并把它们解包.注意必须与它们在函数里的参数相吻合.当然,你也可以在函数定义或者函数调用时用*.
这个AOP一听起来有点懵,同学面阿里的时候就被问懵了…
装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”
我们并不关心对象是什么类型,到底是不是鸭子,只关心行为。
比如在python中,有很多file-like的东西,比如StringIO,GzipFile,socket。它们有很多相同的方法,我们把它们当作文件使用。
又比如list.extend()方法中,我们并不关心它的参数是不是list,只要它是可迭代的,所以它的参数可以是list/tuple/dict/字符串/生成器等.
鸭子类型在动态语言中经常使用,非常灵活,使得python不想java那样专门去弄一大堆的设计模式。
函数重载主要是为了解决两个问题。
另外,一个基本的设计原则是,仅仅当两个函数除了参数类型和参数个数不同以外,其功能是完全相同的,此时才使用函数重载,如果两个函数的功能其实不同,那么不应当使用重载,而应当使用一个名字不同的函数。
好吧,那么对于情况1,函数功能相同,但是参数类型不同,python如何处理?答案是根本不需要处理,因为python可以接受任何类型的参数,如果函数的功能相同,那么不同的参数类型在python中很可能是相同的代码,没有必要做成两个不同函数。
那么对于情况2,函数功能相同,但参数个数不同,python如何处理?大家知道,答案就是缺省参数。对那些缺少的参数设定为缺省参数即可解决问题。因为你假设函数功能相同,那么那些缺少的参数终归是需要用的。
好了,鉴于情况1跟情况2都有了解决方案,python自然就不需要函数重载了。
这个面试官问了,我说了老半天,不知道他问的真正意图是什么.
新式类很早在2.2就出现了,所以旧式类完全是兼容的问题,Python3里的类全部都是新式类.这里有一个MRO问题可以了解下(新式类是广度优先,旧式类是深度优先),
一个旧式类的深度优先的例子
classA():deffoo1(self):print"A"classB(A):deffoo2(self):passclassC(A):deffoo1(self):print"C"classD(B,C):passd=D()d.foo1()#A按照经典类的查找顺序从左到右深度优先的规则,在访问d.foo1()的时候,D这个类是没有的…那么往上查找,先找到B,里面没有,深度优先,访问A,找到了foo1(),所以这时候调用的是A的foo1(),从而导致C重写的foo1()被绕过
这个__new__确实很少见到,先做了解吧.
ps:__metaclass__是创建类时起作用.所以我们可以分别使用__metaclass__,__new__和__init__来分别在类创建,实例创建和实例初始化的时候做一些小手脚.
单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
__new__()在__init__()之前被调用,用于生成实例对象。利用这个方法和类的属性的特点可以实现设计模式的单例模式。单例模式是指创建唯一对象,单例模式设计的类只能实例这个绝对常考啊.绝对要记住1~2个方法,当时面试官是让手写的.
classSingleton(object):def__new__(cls,*args,**kw):ifnothasattr(cls,'_instance'):orig=super(Singleton,cls)cls._instance=orig.__new__(cls,*args,**kw)returncls._instanceclassMyClass(Singleton):a=12共享属性创建实例时把所有实例的__dict__指向同一个字典,这样它们具有相同的属性和方法.
classBorg(object):_state={}def__new__(cls,*args,**kw):ob=super(Borg,cls).__new__(cls,*args,**kw)ob.__dict__=cls._statereturnobclassMyClass2(Borg):a=13装饰器版本defsingleton(cls):instances={}defgetinstance(*args,**kw):ifclsnotininstances:instances[cls]=cls(*args,**kw)returninstances[cls]returngetinstance@singletonclassMyClass:...4import方法作为python的模块是天然的单例模式
Python中,一个变量的作用域总是由在代码中被赋值的地方所决定的。
当Python遇到一个变量的话他会按照这样的顺序进行搜索:
本地作用域(Local)当前作用域被嵌入的本地作用域(Enclosinglocals)全局/模块作用域(Global)内置作用域(Built-in)
解决办法就是多进程和下面的协程(协程也只是单CPU,但是能减小切换代价提升性能).
知乎被问到了,呵呵哒,跪了
Python里最常见的yield就是协程的思想!可以查看第九个问题.
闭包(closure)是函数式编程的重要的语法结构。闭包也是一种组织代码的结构,它同样提高了代码的可重复使用性。
当一个内嵌函数引用其外部作作用域的变量,我们就会得到一个闭包.总结一下,创建一个闭包必须满足以下几点:
重点是函数运行后并不会被撤销,就像16题的instance字典一样,当函数运行完后,instance并不被销毁,而是继续留在内存空间里.这个功能类似类里的类变量,只不过迁移到了函数上.
闭包就像个空心球一样,你知道外面和里面,但你不知道中间是什么样.
其实就是一个匿名函数,为什么叫lambda因为和后面的函数式编程有关.
这个需要适当的了解一下吧,毕竟函数式编程在Python中也做了引用.
python中函数式编程支持:
filter函数的功能相当于过滤器。调用一个布尔函数bool_func来迭代遍历每个seq中的元素;返回一个使bool_seq返回值为true的元素的序列。
>>>a=[1,2,3,4,5,6,7]>>>b=filter(lambdax:x>5,a)>>>printb>>>[6,7]map函数是对一个序列的每个项依次执行函数,下面是对一个序列每个项都乘以2:
>>>a=map(lambdax:x*2,[1,2,3])>>>list(a)[2,4,6]reduce函数是对一个序列的每个项迭代调用函数,下面是求3的阶乘:
>>>reduce(lambdax,y:x*y,range(1,4))623Python里的拷贝引用和copy(),deepcopy()的区别
PyObject是每个对象必有的内容,其中ob_refcnt就是做为引用计数。当一个对象有新的引用时,它的ob_refcnt就会增加,当引用它的对象被删除,它的ob_refcnt就会减少.引用计数为0时,该对象生命就结束了。
优点:
缺点:
基本思路是先按需分配,等到没有空闲内存的时候从寄存器和程序栈上的引用出发,遍历以对象为节点、以引用为边构成的图,把所有可以访问到的对象打上标记,然后清扫一遍内存空间,把所有没标记的对象释放。
is是对比地址,==是对比值
super()letsyouavoidreferringtothebaseclassexplicitly,whichcanbenice.Butthemainadvantagecomeswithmultipleinheritance,whereallsortsoffunstuffcanhappen.Seethestandarddocsonsuperifyouhaven’talready.
NotethatthesyntaxchangedinPython3.0:youcanjustsaysuper().__init__()insteadofsuper(ChildB,self).__init__()whichIMOisquiteabitnicer.
都在循环时使用,xrange内存性能更好。foriinrange(0,20):foriinxrange(0,20):WhatisthedifferencebetweenrangeandxrangefunctionsinPython2.Xrangecreatesalist,soifyoudorange(1,10000000)itcreatesalistinmemorywith9999999elements.xrangeisasequenceobjectthatevaluateslazily.
其实所有的I/O都是轮询的方法,只不过实现的层面不同罢了.
这个问题可能有点深入了,但相信能回答出这个问题是对I/O多路复用有很好的了解了.其中tornado使用的就是epoll的.
基本上select有3个缺点:
poll改善了第一个缺点
epoll改了三个缺点.
实时调度算法:
原因:
必要条件:
处理死锁基本方法:
Bulid过程可以分解为4个步骤:预处理(Prepressing),编译(Compilation)、汇编(Assembly)、链接(Linking)
以c语言为例:
预编译过程主要处理那些源文件中的以“#”开始的预编译指令,主要处理规则有:
编译过程就是把预处理完的文件进行一系列的词法分析、语法分析、语义分析及优化后生成相应的汇编代码文件。这个过程是整个程序构建的核心部分。
汇编器是将汇编代码转化成机器可以执行的指令,每一条汇编语句几乎都是一条机器指令。经过编译、链接、汇编输出的文件成为目标文件(ObjectFile)
链接的主要内容就是把各个模块之间相互引用的部分处理好,使各个模块可以正确的拼接。链接的主要过程包块地址和空间的分配(AddressandStorageAllocation)、符号决议(SymbolResolution)和重定位(Relocation)等步骤。
静态链接方法:静态链接的时候,载入代码就会把程序会用到的动态代码或动态代码的地址确定下来静态库的链接可以使用静态链接,动态链接库也可以使用这种方法链接导入库
虚拟存储器是指具有请求调入功能和置换功能,能从逻辑上对内存容量加以扩充的一种存储系统.
分页:用户程序的地址空间被划分成若干固定大小的区域,称为“页”,相应地,内存空间分成若干个物理块,页和块的大小相等。可将用户程序的任一页放在内存的任一块中,实现了离散分配。
分段:将用户程序地址空间分成若干个大小不等的段,每段可以定义一组相对完整的逻辑信息。存储分配时,以段为单位,段与段在内存中可以不相邻接,也实现了离散分配。
边缘触发是指每当状态变化时发生一个io事件,条件触发是只要满足条件就发生一个io事件
聚集索引,非聚集索引,B-Tree,B+Tree,最左前缀原理
通常局限点来说,Redis也以消息队列的形式存在,作为内嵌的List存在,满足实时的高并发需求。在使用缓存的时候,redis比memcached具有更多的优势,并且支持更多的数据类型,把redis当作一个中间存储系统,用来处理高并发的数据库操作
悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作
乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。
其中,写操作(insert、delete和update)执行时,需要将系统版本号递增。
由于旧数据并不真正的删除,所以必须对这些数据进行清理,innodb会开启一个后台线程执行清理工作,具体的规则是将删除版本号小于当前系统版本的行删除,这个过程叫做purge。
通过MVCC很好的实现了事务的隔离性,可以达到repeatedread级别,要实现serializable还必须加锁。
MyISAM适合于一些需要大量查询的应用,但其对于有大量写操作并不是很好。甚至你只是需要update一个字段,整个表都会被锁起来,而别的进程,就算是读进程都无法操作直到读操作完成。另外,MyISAM对于SELECTCOUNT(*)这类的计算是超快无比的。
InnoDB的趋势会是一个非常复杂的存储引擎,对于一些小的应用,它会比MyISAM还慢。他是它支持“行锁”,于是在写操作比较多的时候,会更优秀。并且,他还支持更多的高级应用,比如:事务。
注意:中断连接端可以是客户端,也可以是服务器端.下面仅以客户端断开连接举例,反之亦然.
地址解析协议(AddressResolutionProtocol),其基本功能为透过目标设备的IP地址,查询目标的MAC地址,以保证通信的顺利进行。它是IPv4网络层必不可少的协议,不过在IPv6中已不再适用,并被邻居发现协议(NDP)所替代。
这个面试官确实问过,当时答的urllib2可以Post而urllib不可以.
session技术是要使用到cookie的,之所以出现session技术,主要是为了安全。
nginx相对apache的优点:
apache相对nginx的优点:
403:Forbidden404:NotFound
HTTPS握手,对称加密,非对称加密,TLS/SSL,RSA
CSRF重点在请求,XSS重点在脚本
HTTP方法的幂等性是指一次和多次请求某一个资源应该具有同样的副作用。(注意是副作用)
RPC(RemoteProcedureCallProtocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。
进化的顺序:RPC->SOAP->RESTful
CGI是通用网关接口,是连接web服务器和应用程序的接口,用户通过CGI来获取动态数据或文件等。CGI程序是一个独立的程序,它可以用几乎所有语言来写,包括perl,c,lua,python等等。
WSGI,WebServerGatewayInterface,是Python应用程序或框架和Web服务器之间的一种接口,WSGI的其中一个目的就是让用户可以用统一的语言(Python)编写前后端。
在GFW里屡见不鲜的,呵呵.
中间人攻击(Man-in-the-middleattack,通常缩写为MITM)是指攻击者与通讯的两端分别创建独立的联系,并交换其所收到的数据,使通讯的两端认为他们正在通过一个私密的连接与对方直接对话,但事实上整个会话都被攻击者完全控制。
Socket=Ipaddress+TCP/UDP+port
304NotModified
HTTP请求8种方法介绍HTTP/1.1协议中共定义了8种HTTP请求方法,HTTP请求方法也被叫做“请求动作”,不同的方法规定了不同的操作指定的资源方式。服务端也会根据不同的请求方法做不同的响应。
GET
GET请求会显示请求指定的资源。一般来说GET方法应该只用于数据的读取,而不应当用于会产生副作用的非幂等的操作中。
GET会方法请求指定的页面信息,并返回响应主体,GET被认为是不安全的方法,因为GET方法会被网络蜘蛛等任意的访问。
HEAD
HEAD方法与GET方法一样,都是向服务器发出指定资源的请求。但是,服务器在响应HEAD请求时不会回传资源的内容部分,即:响应主体。这样,我们可以不传输全部内容的情况下,就可以获取服务器的响应头信息。HEAD方法常被用于客户端查看服务器的性能。
POST
POST请求会向指定资源提交数据,请求服务器进行处理,如:表单数据提交、文件上传等,请求数据会被包含在请求体中。POST方法是非幂等的方法,因为这个请求可能会创建新的资源或/和修改现有资源。
PUT
PUT请求会身向指定资源位置上传其最新内容,PUT方法是幂等的方法。通过该方法客户端可以将指定资源的最新数据传送给服务器取代指定的资源的内容。
DELETE
DELETE请求用于请求服务器删除所请求URI(统一资源标识符,UniformResourceIdentifier)所标识的资源。DELETE请求后指定资源会被删除,DELETE方法也是幂等的。
CONNECT
CONNECT方法是HTTP/1.1协议预留的,能够将连接改为管道方式的代理服务器。通常用于SSL加密服务器的链接与非加密的HTTP代理服务器的通信。
OPTIONS
OPTIONS请求与HEAD类似,一般也是用于客户端查看服务器的性能。这个方法会请求服务器返回该资源所支持的所有HTTP请求方法,该方法会用’*’来代替资源名称,向服务器发送OPTIONS请求,可以测试服务器功能是否正常。JavaScript的XMLHttpRequest对象进行CORS跨域资源共享时,就是使用OPTIONS方法发送嗅探请求,以判断是否有对指定资源的访问权限。允许
TRACE
TRACE请求服务器回显其收到的请求信息,该方法主要用于HTTP请求的测试或诊断。
HTTP/1.1之后增加的方法
在HTTP/1.1标准制定之后,又陆续扩展了一些方法。其中使用中较多的是PATCH方法:
PATCH
PATCH方法出现的较晚,它在2010年的RFC5789标准中被定义。PATCH请求与PUT请求类似,同样用于资源的更新。二者有以下两点不同:
但PATCH一般用于资源的部分更新,而PUT一般用于资源的整体更新。当资源不存在时,PATCH会创建一个新的资源,而PUT只会对已在资源进行更新。
AJAX,AsynchronousJavaScriptandXML(异步的JavaScript和XML),是与在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页的技术。
红黑树与AVL的比较:
AVL是严格平衡树,因此在增加或者删除节点的时候,根据不同情况,旋转的次数比红黑树要多;
红黑是用非严格的平衡来换取增删节点时候旋转次数的降低;
所以简单说,如果你的应用中,搜索的次数远远大于插入和删除,那么选择AVL,如果搜索,插入删除次数几乎差不多,应该选择RB。
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
fib=lambdan:nifn<=2elsefib(n-1)+fib(n-2)第二种记忆方法
defmemo(func):cache={}defwrap(*args):ifargsnotincache:cache[args]=func(*args)returncache[args]returnwrap@memodeffib(i):ifi<2:return1returnfib(i-1)+fib(i-2)第三种方法
deffib(n):a,b=0,1for_inxrange(n):a,b=b,a+breturnb2变态台阶问题一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
fib=lambdan:nifn<2else2*fib(n-1)3矩形覆盖我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
第2*n个矩形的覆盖方法等于第2*(n-1)加上第2*(n-2)的方法。
f=lambdan:1ifn<2elsef(n-1)+f(n-2)4杨氏矩阵查找在一个m行n列二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
使用Step-wise线性搜索。
defget_value(l,r,c):returnl[r][c]deffind(l,x):m=len(l)-1n=len(l[0])-1r=0c=nwhilec>=0andr<=m:value=get_value(l,r,c)ifvalue==x:returnTrueelifvalue>x:c=c-1elifvalue list(set(l))用字典 l1=['b','c','d','b','c','a','a']l2={}.fromkeys(l1).keys()printl2用字典并保持顺序 l1=['b','c','d','b','c','a','a']l2=list(set(l1))l2.sort(key=l1.index)printl2列表推导式 l1=['b','c','d','b','c','a','a']l2=[][l2.append(i)foriinl1ifnotiinl2]sorted排序并且用列表推导式. l=[‘b’,‘c’,‘d’,‘b’,‘c’,‘a’,‘a’][single.append(i)foriinsorted(l)ifinotinsingle]printsingle 1->2->3->4转换成2->1->4->3. classListNode:def__init__(self,x):self.val=xself.next=NoneclassSolution:#@paramaListNode#@returnaListNodedefswapPairs(self,head):ifhead!=Noneandhead.next!=None:next=head.nexthead.next=self.swapPairs(next.next)next.next=headreturnnextreturnhead7创建字典的方法1直接创建dict={'name':'earth','port':'80'}2工厂方法items=[('name','earth'),('port','80')]dict2=dict(items)dict1=dict((['name','earth'],['port','80']))3fromkeys()方法dict1={}.fromkeys(('x','y'),-1)dict={'x':-1,'y':-1}dict2={}.fromkeys(('x','y'))dict2={'x':None,'y':None}8合并两个有序列表知乎远程面试要求编程 尾递归 def_recursion_merge_sort2(l1,l2,tmp):iflen(l1)==0orlen(l2)==0:tmp.extend(l1)tmp.extend(l2)returntmpelse:ifl1[0] 思路: 定义一个新的空列表 比较两个列表的首个元素 小的就插入到新列表里 把已经插入新列表的元素从旧列表删除 直到两个旧列表有一个为空 再把旧列表加到新列表后面 defloop_merge_sort(l1,l2):tmp=[]whilelen(l1)>0andlen(l2)>0:ifl1[0] a=[1,2,3,7]b=[3,4,5]defmerge_sortedlist(a,b):c=[]whileaandb:ifa[0]>=b[0]:c.append(b.pop(0))else:c.append(a.pop(0))whilea:c.append(a.pop(0))whileb:c.append(b.pop(0))returncprintmerge_sortedlist(a,b)9交叉链表求交点其实思想可以按照从尾开始比较两个链表,如果相交,则从尾开始必然一致,只要从尾开始比较,直至不一致的地方即为交叉点,如图所示 #使用a,b两个list来模拟链表,可以看出交叉点是7这个节点a=[1,2,3,7,9,1,5]b=[4,5,7,9,1,5]foriinrange(1,min(len(a),len(b))):ifi==1and(a[-1]!=b[-1]):print"No"breakelse:ifa[-i]!=b[-i]:print"交叉节点:",a[-i+1]breakelse:pass另外一种比较正规的方法,构造链表类 classListNode:def__init__(self,x):self.val=xself.next=Nonedefnode(l1,l2):length1,lenth2=0,0#求两个链表长度whilel1.next:l1=l1.nextlength1+=1whilel2.next:l2=l2.nextlength2+=1#长的链表先走iflength1>lenth2:for_inrange(length1-length2):l1=l1.nextelse:for_inrange(length2-length1):l2=l2.nextwhilel1andl2:ifl1.next==l2.next:returnl1.nextelse:l1=l1.nextl2=l2.next修改了一下: 给定一个数组,构建二叉树,并且按层次打印这个二叉树 classNode(object):def__init__(self,data,left=None,right=None):self.data=dataself.left=leftself.right=righttree=Node(1,Node(3,Node(7,Node(0)),Node(6)),Node(2,Node(5),Node(4)))15层次遍历deflookup(root):row=[root]whilerow:print(row)row=[kidforiteminrowforkidin(item.left,item.right)ifkid]16深度遍历defdeep(root):ifnotroot:returnprintroot.datadeep(root.left)deep(root.right)if__name__=='__main__':lookup(tree)deep(tree)