工作久了对之前的基础语法做一个整理,以便自己更了解python的语法, 在编码的时候可以更加更加得心应手。
1. Python函数参数传递
先看两个例子
In [20]: a = 1
In [21]: id(a)
Out[21]: 4482033600
In [22]: def func(a):
...: a = 2
...: print(id(a), a)
...:
In [23]: func(a)
4482033632 2
In [24]:
复制代码
所有的变量都可以理解是内存中一个对象的“引用”, 通过id查看变量内存地址
在命名变量a查看的id和在函数中变量a的id是不一样的, 所以作用域的不同会生成两个不同的内存地址
In [24]: b = 1
In [25]: def fun(b):
...: b = 2
...: print(id(b), b)
...:
In [26]: fun(b)
4482033632 2
In [27]:
复制代码
在定义b变量之后查看id 和上一个a是类似的关系, 但是当查看全局变量b的时候发现id和第一个例子中函数中a变量的内存地址相同, 所以在python中当值类型相同时, 指针会指向同一个内存地址以减少内存的使用。
2. Python变量
在Python中有常见的string, int, list,float, tuple, dic
其中不可变对象: string, int , float, tuple, 可变对象: list, dictionary
在python中变量存放的是对象引用,虽然对象引用是可变的, 但是对象本身是不变的, 指针不会指向新的内存空间.
3. @staticmethod 和 @classmethod
在python中方法的类型有静态方法, 类方法和实例方法
代码如下:
def foo(x):
print(x)
class A(object):
""" 创建类 """
def foo(self, x):
# 实例方法
...
@classmethod
def class_foo(cls, x)
# 类方法
...
@staticmethod
def static_foo(x):
# 静态方法
...
a = A()
a.foo(1)
A.class_foo(1)
A.static(1)
a.class_foo(1)
a.static(1)
复制代码
创建A类的时候实例方法必须是要创建一个实例a, 并且通过实例a调用实例方法, 但是不能直接用过A类直接调用, 但是实例a通过也可以调用A类的静态方法和类方法。
4. 类变量和实例变量
类变量: 在创建类的时候定义,并且是所有实例之间共享的值.
实例变量: 实例化之后, 每个实例单独拥有的变量,在创建实例中传值
In [40]: class A(object):
...: num = 0
...: def __init__(self, name):
...: self.name = name
...: A.num += 1
...: print(name, A.num)
...:
...:
...:
In [41]: a = A('jay')
jay 1
In [42]: b = A('jack')
jack 2
In [43]: A.num
Out[43]: 2
In [44]:
复制代码
5.Python自省模式
在定义变量的时候不用声明变量类型, 在程序运行时需要动态获取对象类型, type(),dir(),getattr(),hasattr(),isinstance().
6.字典推导式和列表推导式
v = {key: value for (key, value) in iterable }
l = [x for x in list]
复制代码
7.单下划线和双下划线
In [45]: class B(object):
...: def __init__(self):
...: self.__superprivate = 'hello'
...: self._semprivate = 'word'
...:
In [46]: m = B()
In [49]: m.__dict__
Out[49]: {'_B__superprivate': 'hello', '_semprivate': 'word'}
复制代码
双下划线__foo__ 类型: python内部函数的命名方式,用来区别其他用户自定义的命名冲突,常见的__init__,__new__等等,
_foo: 类的私有变量命名方式,在该类被实例化或者被引用的时候访问不到的变量
__foo: 解析器会用_classname__foo来替代这个名字,以区别和其他类重复命名的冲突,通过对象名._类名__xxx这样的方式可以访问.
8. 迭代器和生成器
在创建列表时,由于受到内存限制, 列表容量有限, 当一次性生成大量数据的列表时会造成大量的内存浪费, 所以在python中采用生成器: 边循环边计算的机制 -> generator
>>> L = [x*x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x*x for x in range(10))
>>> g
<generator object <genexpr> at 0x0000028F8B774200>
复制代码
9. * args和**kwargs
用*args和**kwargs只是为了方便并没有强制使用它们.
当你不确定你的函数里将要传递多少参数时你可以用*args.例如,它可以传递任意数量的参数:
>>> def print_everything(*args):
for count, thing in enumerate(args):
... print '{0}. {1}'.format(count, thing)
...
>>> print_everything('apple', 'banana', 'cabbage')
0. apple
1. banana
2. cabbage
相似的,**kwargs允许你使用没有事先定义的参数名:
>>> def table_things(**kwargs):
... for name, value in kwargs.items():
... print '{0} = {1}'.format(name, value)
...
>>> table_things(apple = 'fruit', cabbage = 'vegetable')
cabbage = vegetable
apple = fruit
你也可以混着用.命名参数首先获得参数值然后所有的其他参数都传递给*args和**kwargs.命名参数在列表的最前端.例如:
def table_things(titlestring, **kwargs)
复制代码
args和**kwargs可以同时在函数的定义中,但是args必须在**kwargs前面.
当调用函数时你也可以用*和**语法.例如:
>>> def print_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
复制代码
就像你看到的一样,它可以传递列表(或者元组)的每一项并把它们解包.注意必须与它们在函数里的参数相吻合.当然,你也可以在函数定义或者函数调用时用*.
10. 面向切面编程AOP和装饰器
装饰器是一个非常有用的设计, 经常被用于有切面需求的场景, 较为经典的的插入日志,性能测试、事务处理等。可以抽离出大量函数中与函数本身无关的雷同代码病继续重用, 其实就是给函数添加额外功能。
11. 鸭子类型
“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”
我们并不关心对象是什么类型,到底是不是鸭子,只关心行为。
比如在python中,有很多file-like的东西,比如StringIO,GzipFile,socket。它们有很多相同的方法,我们把它们当作文件使用。
又比如list.extend()方法中,我们并不关心它的参数是不是list,只要它是可迭代的,所以它的参数可以是list/tuple/dict/字符串/生成器等.
鸭子类型在动态语言中经常使用,非常灵活,使得python不想java那样专门去弄一大堆的设计模式。
12.Python中的重载
之所以要重载, 主要是为了改变可变参数类型或者可变参数个数,面向对象思想了解的情况下很容易理解重载。
13. 新式类和旧式类
这篇文章很好的介绍了新式类的特性: www.cnblogs.com/btchenguang…
新式类很早在2.2就出现了,所以旧式类完全是兼容的问题,Python3里的类全部都是新式类.这里有一个MRO问题可以了解下(新式类继承是根据C3算法,旧式类是深度优先),<Python核心编程>里讲的也很多.
一个旧式类的深度优先的例子
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()
# A
复制代码
按照经典类的查找顺序从左到右深度优先的规则,在访问d.foo1()的时候,D这个类是没有的..那么往上查找,先找到B,里面没有,深度优先,访问A,找到了foo1(),所以这时候调用的是A的foo1(),从而导致C重写的foo1()被绕过
14. __new__和__init__的区别
new__是一个静态方法,而__init__是一个实例方法. new__方法会返回一个创建的实例,而__init__什么都不返回. 只有在__new__返回一个cls的实例时后面的__init__才能被调用. 当创建一个新实例时调用__new,初始化一个实例时用__init.
ps: metaclass__是创建类时起作用.所以我们可以分别使用__metaclass,__new__和__init__来分别在类创建,实例创建和实例初始化的时候做一些小手脚.
15. 单例模式
单例模式是一种软件常用的设计模式, 在它核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证一个类中只有一个实例并且这个实例易于被外界访问, 从而对实例个数的控制并且节约系统内存资源 new()在__init__()之前被调用,用于生成实例对象。利用这个方法和类的属性的特点可以实现设计模式的单例模式。单例模式是指创建唯一对象,单例模式设计的类只能实例 这个绝对常考啊.绝对要记住1~2个方法,当时面试官是让手写的.
单例模式的实现方法:
- 使用__new__方法
class Singleton(object):
def __new__(cls, *args, **kwargs):
if not hasttr(cls, '_instance'):
orig = super(Singleton, cls)
cls._instance = orig.__new__(cls, *args, **kwargs)
return cls._instance
class MyClass(Singleton):
a = 1
复制代码
- 共享模式 创建实例时把所有实例的__dict__指向同一个字典, 这样它们具有相同的属性和方法。
class Borg(object):
_state = {}
def __new__(cls, *args, **kw):
ob = super(Borg, cls).__new__(cls, *args, **kw)
ob.__dict__ = cls._state
return ob
class MyClass2(Borg):
a = 1
复制代码
- 装饰器版本
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:
...
复制代码
- 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()
复制代码
Python中的作用域
Python中, 一个变量的作用域总是在代码中被赋值的地方所决定的。
当Python遇到一个变量的话他会按照这样的顺序进行搜索
本地作用域(local) -> 当前作用域被嵌入的本地作用域( Enclosing locals) -> 全局/模块作用域(built-in)
18 GIL线程全局锁
线程全局锁(Global Interpreter Lock),即Python为了保证线程安全而采取的独立线程运行的限制,说白了就是一个核只能在同一时间运行一个线程.对于io密集型任务,python的多线程起到作用,但对于cpu密集型任务,python的多线程几乎占不到任何优势,还有可能因为争夺资源而变慢。
解决办法就是多进程和下面的协程(协程也只是单CPU,但是能减小切换代价提升性能).
19. 协程
简单点说协程是进程和线程的升级版,进程和线程都面临着内核态和用户态的切换问题而耗费许多切换时间,而协程就是用户自己控制切换的时机,不再需要陷入系统的内核态.
20. 闭包
闭包(closure)是函数式编程的重要的语法结构。闭包也是一种组织代码的结构,它同样提高了代码的可重复使用性。
当一个内嵌函数引用其外部作作用域的变量,我们就会得到一个闭包. 总结一下,创建一个闭包必须满足以下几点:
必须有一个内嵌函数 内嵌函数必须引用外部函数中的变量 外部函数的返回值必须是内嵌函数 感觉闭包还是有难度的,几句话是说不明白的,还是查查相关资料.
重点是函数运行后并不会被撤销,就像16题的instance字典一样,当函数运行完后,instance并不被销毁,而是继续留在内存空间里.这个功能类似类里的类变量,只不过迁移到了函数上.
闭包就像个空心球一样,你知道外面和里面,但你不知道中间是什么样.