diff --git a/Readme.md b/Readme.md index 5d71338..4c56019 100644 --- a/Readme.md +++ b/Readme.md @@ -116,6 +116,12 @@ * [21 单链表逆置](#21-单链表逆置) * [22 两个字符串是否是变位词](#22-两个字符串是否是变位词) * [23 动态规划问题](#23-动态规划问题) + * [TODO] + * python magic + * mongo + * 遇到过最坑的问题,怎么解决的 + * 最近研究的技术,看的书,对哪块研究最深; + * py2.7 vs py3 @@ -191,7 +197,7 @@ print a # [1] Python其实有3个方法,即静态方法(staticmethod),类方法(classmethod)和实例方法,如下: ```python -def foo(x): +def outer_foo(x): print "executing foo(%s)"%(x) class A(object): @@ -210,15 +216,30 @@ a=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)`来调用. - -| \\ | 实例方法 | 类方法 | 静态方法 | +| \\ | 实例方法 | 类方法 | 静态方法 | | :------ | :------- | :------------- | :-------------- | | a = A() | a.foo(x) | a.class_foo(x) | a.static_foo(x) | | A | 不可用 | A.class_foo(x) | A.static_foo(x) | +**静态方法的两个函数的地址相同,指向同一个函数对象** +`a.static_foo` 与 `A.static_foo` + +outer_foo vs static_foo:二者本质是是一样的。 +- outer_foo 外部函数,所有类或者方法都可以共用 +- static_foo 只有A类或者其实例才可以用。 + + +foo vs class_foo: +- foo 实例调用 +- class_foo:第一个参数时cls,类或者实例调用。实例(可看做先有类,在有实例,所以本质上也是有类) + +static_foo vs class_foo +- static_foo:可以少传cls参数,减少类的开支。 + +对于静态方法其实和普通的方法一样,不需要对谁进行绑定,唯一的区别是调用的时候需要使用`a.static_foo(x)`或者`A.static_foo(x)`来调用. + + + 更多关于这个问题: 1. http://stackoverflow.com/questions/136097/what-is-the-difference-between-staticmethod-and-classmethod-in-python 2. https://realpython.com/blog/python/instance-class-and-static-methods-demystified/ @@ -349,6 +370,21 @@ AttributeError: myClass instance has no attribute '__superprivate' 但是有点丑..format就没有这些问题.你给的第二个问题也是这样,.format好看多了. +format用法 +```python +sub1 = "python string!" +sub2 = "an arg" + +a = "i am a %s" % sub1 +b = "i am a {0}".format(sub1) + +c = "with %(kwarg)s!" % {'kwarg':sub2} +d = "with {kwarg}!".format(kwarg=sub2) + +tu = (12,45,22222,103,6) +print '{0} {2} {1} {2} {3} {2} {4} {2}'.format(*tu) +``` + 你为什么不用它? * 不知道它(在读这个之前) @@ -366,6 +402,7 @@ http://stackoverflow.com/questions/5082452/python-string-formatting-vs-format 问: 将列表生成式中[]改成() 之后数据结构是否改变? 答案:是,从列表变为生成器 + ```python >>> L = [x*x for x in range(10)] >>> L @@ -374,6 +411,27 @@ http://stackoverflow.com/questions/5082452/python-string-formatting-vs-format >>> g at 0x0000028F8B774200> ``` + +Yield +yield is a keyword that is used like return, except the function will return a generator. + +```shell + +>>> def createGenerator(): +... mylist = range(3) +... for i in mylist: +... yield i*i +... +>>> mygenerator = createGenerator() # create a generator +>>> print(mygenerator) # mygenerator is an object! + +>>> for i in mygenerator: +... print(i) +0 +1 +4 +``` + 通过列表生成式,可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含百万元素的列表,不仅是占用很大的内存空间,如:我们只需要访问前面的几个元素,后面大部分元素所占的空间都是浪费的。因此,没有必要创建完整的列表(节省大量内存空间)。在Python中,我们可以采用生成器:边循环,边计算的机制—>generator ## 10 `*args` and `**kwargs` @@ -435,6 +493,63 @@ http://stackoverflow.com/questions/3394835/args-and-kwargs 装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。概括的讲,**装饰器的作用就是为已经存在的对象添加额外的功能。** +手写装饰器 + +```python +# A decorator is a function that expects ANOTHER function as parameter +def my_shiny_new_decorator(a_function_to_decorate): + + # Inside, the decorator defines a function on the fly: the wrapper. + # This function is going to be wrapped around the original function + # so it can execute code before and after it. + def the_wrapper_around_the_original_function(): + + # Put here the code you want to be executed BEFORE the original function is called + print("Before the function runs") + + # Call the function here (using parentheses) + a_function_to_decorate() + + # Put here the code you want to be executed AFTER the original function is called + print("After the function runs") + + # At this point, "a_function_to_decorate" HAS NEVER BEEN EXECUTED. + # We return the wrapper function we have just created. + # The wrapper contains the function and the code to execute before and after. It’s ready to use! + return the_wrapper_around_the_original_function +``` +或者 +```python +from functools import wraps + +def makebold(fn): + @wraps(fn) + def wrapped(*args, **kwargs): + return "" + fn(*args, **kwargs) + "" + return wrapped + +def makeitalic(fn): + @wraps(fn) + def wrapped(*args, **kwargs): + return "" + fn(*args, **kwargs) + "" + return wrapped + +@makebold +@makeitalic +def hello(): + return "hello world" + +@makebold +@makeitalic +def log(s): + return s + +print hello() # returns "hello world" +print hello.__name__ # with functools.wraps() this returns "hello" +print log('hello') # returns "hello" +``` + + 这个问题比较大,推荐: http://stackoverflow.com/questions/739654/how-can-i-make-a-chain-of-function-decorators-in-python 中文: http://taizilongxu.gitbooks.io/stackoverflow-about-python/content/3/README.html @@ -451,6 +566,9 @@ http://stackoverflow.com/questions/3394835/args-and-kwargs 鸭子类型在动态语言中经常使用,非常灵活,使得python不想java那样专门去弄一大堆的设计模式。 +**鸭子类型 的使用:** +从上面可以看出,python鸭子类型的灵活性在于它关注的是这个所调用的对象是如何被使用的,而没有关注对象类型的本身是什么。所以在python中使用isinstance来判断传入参数的类型是不提倡的,更pythonic的方法是直接使用传入的参数,通过try,except来处理传入参数不符合要求的情况。我们应该通过传入对象的能力而不是传入对象的类型来使用该对象。 + ## 13 Python中重载 引自知乎:http://www.zhihu.com/question/20053359 @@ -466,7 +584,7 @@ http://stackoverflow.com/questions/3394835/args-and-kwargs 那么对于情况 2 ,函数功能相同,但参数个数不同,python 如何处理?大家知道,答案就是缺省参数。对那些缺少的参数设定为缺省参数即可解决问题。因为你假设函数功能相同,那么那些缺少的参数终归是需要用的。 -好了,鉴于情况 1 跟 情况 2 都有了解决方案,python 自然就不需要函数重载了。 +好了,鉴于情况 1 跟 情况 2 都有了解决方案,**python 自然就不需要函数重载了**。 ## 14 新式类和旧式类 @@ -474,7 +592,7 @@ http://stackoverflow.com/questions/3394835/args-and-kwargs [stackoverflow](http://stackoverflow.com/questions/54867/what-is-the-difference-between-old-style-and-new-style-classes-in-python) -这篇文章很好的介绍了新式类的特性: http://www.cnblogs.com/btchenguang/archive/2012/09/17/2689146.html +这篇文章很好的介绍了新式类的特性: http://www.cnblogs.com/btchenguang/archive/2012/09/17/2689146.html 👍 新式类很早在2.2就出现了,所以旧式类完全是兼容的问题,Python3里的类全部都是新式类.这里有一个MRO问题可以了解下(新式类继承是根据C3算法,旧式类是深度优先),里讲的也很多. @@ -507,6 +625,12 @@ d.foo1() 这个`__new__`确实很少见到,先做了解吧. +```shell +__new__,__init__方法 +这两个方法是用来创建object的子类对象,静态方法__new__()用来创建类的实例,然后再调用 +__init__()来初始化实例 +``` + 1. `__new__`是一个静态方法,而`__init__`是一个实例方法. 2. `__new__`方法会返回一个创建的实例,而`__init__`什么都不返回. 3. 只有在`__new__`返回一个cls的实例时后面的`__init__`才能被调用. @@ -602,10 +726,25 @@ Python 中,一个变量的作用域总是由在代码中被赋值的地方所 线程全局锁(Global Interpreter Lock),即Python为了保证线程安全而采取的独立线程运行的限制,说白了就是一个核只能在同一时间运行一个线程.**对于io密集型任务,python的多线程起到作用,但对于cpu密集型任务,python的多线程几乎占不到任何优势,还有可能因为争夺资源而变慢。** -见[Python 最难的问题](http://www.oschina.net/translate/pythons-hardest-problem) +墙裂推荐[知乎 python的GIL、多线程、多进程 ](https://zhuanlan.zhihu.com/p/20953544) + +>来源是python设计之初的考虑,为了数据安全所做的决定。 +每个CPU在同一时间只能执行一个线程(在单核CPU下的多线程其实都只是并发,不是并行,并发和并行从宏观上来讲都是同时处理多路请求的概念。但并发和并行又有区别,并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔内发生。) -解决办法就是多进程和下面的协程(协程也只是单CPU,但是能减小切换代价提升性能). +在Python多线程下,每个线程的执行方式: +1.获取GIL +2.执行代码直到sleep或者是python虚拟机将其挂起。 +3.释放GIL +可见,某个线程想要执行,必须先拿到GIL,我们可以把GIL看作是“通行证”,并且在一个python进程中,GIL只有一个。拿不到通行证的线程,就不允许进入CPU执行。 + +> 在python2.x里,GIL的释放逻辑是当前线程遇见IO操作或者ticks计数达到100(ticks可以看作是python自身的一个计数器,专门做用于GIL,每次释放后归零,这个计数可以通过 sys.setcheckinterval 来调整),进行释放。 + + +> 而在python3.x中,GIL不使用ticks计数,改为使用计时器(执行时间达到阈值后,当前线程释放GIL),这样对CPU密集型程序更加友好,但依然没有解决GIL导致的同一时间只能执行一个线程的问题,所以效率依然不尽如人意。 + + +解决办法就是多进程和下面的协程(协程也只是单CPU,但是能减小切换代价提升性能). ## 19 协程 知乎被问到了,呵呵哒,跪了 @@ -615,20 +754,50 @@ Python 中,一个变量的作用域总是由在代码中被赋值的地方所 Python里最常见的yield就是协程的思想!可以查看第九个问题. -## 20 闭包 +[强烈推荐参考](https://juejin.im/post/5c13245ee51d455fa5451f33) +py2 与 py3不同 -闭包(closure)是函数式编程的重要的语法结构。闭包也是一种组织代码的结构,它同样提高了代码的可重复使用性。 -当一个内嵌函数引用其外部作作用域的变量,我们就会得到一个闭包. 总结一下,创建一个闭包必须满足以下几点: +## 20 闭包 + +函数式编程中的,闭包三要素: 1. 必须有一个内嵌函数 2. 内嵌函数必须引用外部函数中的变量 3. 外部函数的返回值必须是内嵌函数 +```python +function count() { + var arr = []; + for (var i=1; i<=3; i++) { + arr.push(function () { + return i * i; + }); + } + return arr; +} + +var results = count(); +var f1 = results[0]; +var f2 = results[1]; +var f3 = results[2]; + +f1(); // 16 +f2(); // 16 +f3(); // 16 + +``` + 感觉闭包还是有难度的,几句话是说不明白的,还是查查相关资料. +- 重点是函数运行后并不会被撤销,就像16题的instance字典一样,当函数运行完后,instance并不被销毁,而是继续留在内存空间里.这个功能类似类里的类变量,只不过迁移到了函数上. +- 只有f()才会执行 +- 因为以上两点,所以也有副作用 -重点是函数运行后并不会被撤销,就像16题的instance字典一样,当函数运行完后,instance并不被销毁,而是继续留在内存空间里.这个功能类似类里的类变量,只不过迁移到了函数上. +闭包的作用: +- 运行时才调用 +- 可以造偏函数,只需要一个参数。 +[资料参考](https://www.liaoxuefeng.com/wiki/1022910821149312/1023021250770016) 闭包就像个空心球一样,你知道外面和里面,但你不知道中间是什么样. ## 21 lambda函数