Python语言特性
1. Python的函数参数传递
看两个如下例子,分析运行结果
#代码1
a = 1
def fun(a):
a = 2
fun(a)
print(a) #1
#代码2
a = []
def fun(a):
a.append(1)
fun(a)
print(a) #[1]
所有的变量都可以理解为内存中的一个对象的引用
,或者也可以看似C中void*的感觉。
这里记住的是类型是属于对象的,而不是变量的。而对象有两种,可更改
(mutable)与不可更改
(immutable)对象。在python中,str
、tuple
、int
是不可更改的对象,而list
、dict
等则是可以修改的对象。
当一个引用传递给函数的时候,函数自动复制一份引用,这个函数的引用和外边的引用没有半毛钱关系,所以第一个例子里函数把引用指向一个不可更改的对象,当函数返回的时候,外面的引用没感觉,而第二个例子就不一样,函数内部的引用指向的是可变对象,对它的操作就和定位了指针地址一样,在内存里进行修改。
2.Python中的元类【metaclass
】
元类就是创建类的东西
。创建类就是为了创建类的实例对象,但是我们已经学习到了python中的类也是对象。元类就是用来创建这些类(对象
)的。元类就是类的类。
3.静态方法@staticmethod和类方法@classmethod
Python其实有3个方法,分别是静态方法、类方法、实例方法。
class A(object):
def foo(self,x):
print("saada%s"%(self,x))
@classmethod
def class_foo(cls,x):
print("asdsad%s"%(cls,x))
@staticmethod
def static_foo(x):
print("adad%s"%(x))
a = A()
先理解函数参数里面的self、cls 这个self和cls是类或者实例的绑定,对于实例方法,我们知道在类里每次定义方法的时候都需要绑定这个实例,就是foo(self,x),为什么要这么做呢?因为实例方法的调用离不开实例,我们需要把实例自己传给函数。调用的时候是这样的a.foo(x)其实foo(a,x)类方法一样,只不过它的传递是类而不是实例A.class_foo(x),注意这里的self和cls可以替换别的参数。
对于静态方法其实和普通方法一样,不需要对谁进行绑定,唯一的区别是调用的时候使用a.statis_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) |
4.类变量和实例变量
class Person:
name = 'aaa'
p1 = Person()
p2 = Person()
p1.name = 'bbb'
print(p1.name)#bbb
print(p2.name)#aaa
print(Person_name)#aaa
类变量
就是供类使用的变量
实例变量
就是供实例使用的变量
这里的p1.name = ‘bbb’是实例调用了类变量,者其实和上面第一个问题一样,就是函数传参的问题,p1.name一开始是指向类变量name=‘aaa’,但是在实例作用域里类变量的引用改变了,就变成了一个实例变量。self.name,不再引用Person的类变量name。
#例子
class Persson:
name = []
p1 = Person()
p2 = Person()
p1.name.append(1)
print(p1.name)#[1]
print(p2.name)#[1]
print(Pseron.name)#[1]
5.Python自省
这属于python彪悍的特性。
自省
就是面向对象的语言所写的程序在运行时,所能知道对象的类型,简单一句就是运行的时候能够知道对象的类型,比如:type
、dir
、getattr
、hasattr
、isinstance
。
6.字典推导式
d = {key:value for (key,value) in iterable}
7.Python中单下划线和双下划线
class MyClass():
def __init__(self):
self.__superprivate = 'Hello'
self._semiprivate = 'world'
mc = MyClass()
print(mc.__superprivate)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: myClass instance has no attribute '__superprivate'
print(mc._semiprivate)
world
print(mc.__dict__)
{'_MyClass__superprivate': 'Hello', '_semiprivate': ', world!'}
__foo__是一种约定,python内部的名字,用来区别其他用户自定义的命名,防止冲突。
_fo是一种约定,用来指定变量私有。
__foo是解析器用_classname__foo来代替这个名字,以区别和其他类相同的命名。
8.字符串格式化%s和format
format
在许多方面更便利,对于%s更烦人的是它无法同时传递一个变量和元组。
"hi %s" % name
如果name是(1,2,3),它将会抛出TypeError
异常,为了保证它总是正确的,你必须这么做
"hi %s"%(name,)#提供一个单元素的数组而不是一个参数
9.迭代器和生成器
在Python中,一边循环中一边计算的机制,称为生成器generator
可以被next
函数调用并不断返回下一个值的对象称为迭代器iterator
10.*args和**args
当不确定函数将要传递多少个参数的时候可以用*args。
def print_1(*args):
for count,thing in enumerate(*args):
print("{0}{1}".format(count,thing))
**args允许你使用没有事先定义的参数名
def print_1(**kwargs):
for name,value in kwargs.items():
print("{0}{1}".format(name,value))
11.面向切面编程AOP和装饰器
装饰器是一种设计模式
经常用于切面需求的应用场景
较为经典的有
- 插入日志
- 性能测试
- 事务处理
装饰器是解决这类问题的绝佳设计。
有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。
装饰器的作用就是为已经存在的对象添加额外的功能。
12 鸭子类型
“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”
我们并不关心对象是什么类型,到底是不是鸭子,只关心行为。
比如在python中,有很多file-like的东西,比如StringIO,GzipFile,socket。它们有很多相同的方法,我们把它们当作文件使用。
又比如list.extend()方法中,我们并不关心它的参数是不是list,只要它是可迭代的,所以它的参数可以是list/tuple/dict/字符串/生成器等.
鸭子类型在动态语言中经常使用,非常灵活,使得python不想java那样专门去弄一大堆的设计模式。
13.重载
函数重载主要是为了解决两个问题
- 可变参数类型
- 可变参数个数
一个基本的设计原则是:仅仅当两个函数除了参数类型和参数个数不同以外。其功能完全相同的,此时才使用函数常在。
如果两个函数的功能不同,不应当使用重载,而应该使用一个名字不同的函数。
问题
- 函数功能相同,但参数类型不同,python如何处理?
- 根本不需要处理,因为python可以接受任何类型的参数,如果函数的功能相同,name不同的参数类型在python中很可能是相同的代码,没有必要做成两个不同的函数。
- 函数功能相同,但参数个数不同,python如何处理?
- 缺省参数,对那些缺少的参数设定为缺省参数即可解决问题。
14.新式类和旧式类
python3里的类全部都是新式类,这里有一个MRO问题,新式类是广度优先,旧式类是深度优先。
15.__new__和__init__的区别
__new__是一个静态方法,而__init__是一个实例方法。
__new__方法会返回一个创建的示例,而__init__什么都不返回
只有在__new__返回一个cls的实例时后面的__init__才能被调用。
当创建一个新实例时调用__new__,初始化一个实例时用__init__
__metaclass__是创建类时起作用,所以我们可以分别使用。
__metaclass__、__new__、__init__来分别在类创建,实例创建和实例初始化的时候做些手脚。
16.单例模式
class A(object):
def __new__(cls,*args,**kwargs):
if not hasattr(cls,'_instance'):
orig = super(A,cls)
cls._instance = orig.__new__(cls,*args,**kwargs)
return cls.__instance
class MyClass(A):
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, *args, **kw):
instances = {}
def getinstance():
if cls not in instances:
instances[cls] = cls(*args, **kw)
return instances[cls]
return getinstance
@singleton
class MyClass:
pass