前言先扯一点背景知识 PEP8(Python Enhancement Proposal)是一份python的编码规范,链接:http://www./dev/peps/pep-0008/ 在这份编码规范中的“命名规范-命名风格”这一节的最后,提到了对几种使用前置和后置下划线的,对变量的比较特殊的命名方式:
本文主要关注正是以上第四种--python自动在用户命名空间创建的magic变量 1、__name__变量__name__属性是直接内置在.py文件中的。
这个属性经常用来当做一个使用模式的标识: #a.pyprint 'a function'if __name__=='__main__': print 'a test'------------------------------#b.pyimport a 如果执行python a.py将打印出两行内容,执行python b.py只会打印出'a function'。一般可以把只针对a.py的测试代码写在if __name__=='__main__',因为如果a.py被其他的脚本import之后,这部分代码将不会被执行。可以很安全的对a.py进行单独的测试。 2、__file__变量__file__可以用来获取python脚本的“路径+脚本名称”,这可能是一个相对路径也可能是一个绝对路径,取决按照什么路径来执行的脚本,一般来说__file__变量和os.path配合,可以用来获取python脚本的绝对路径:
3、__import__函数python导入模块时,一般使用import,而import语句其实也是调用builtin函数:__import__()实现的导入,直接使用__import__比较少见,除非导入的模块是不确定的,需要在运行时才能确定导入哪些模块,可以使用__import__,默认接收需要导入的模块名的字符串: #a.pydef f1(): print 'f1'def f2(): print 'f2'#b.pymodel=__import__('a')model.f1()model.f2() 在memfs的测试中,我的每一个测试case就是一个独立的.py文件,在确定需要测试哪些case后,在运行时才‘动态的’去import相应的case,就是通过__import__来实现的。 4、__str__函数__str__是一个比较常用的内置函数,在定义类的时候经常使用,__str__函数返回一个字符串,这个字符串就是此对象被print时显示的内容,(如果不定义这个函数,将会显示默认的格式:<__main__.A object at 0x0000000001FB7C50>):
这个函数在django的model类中如果定义的话,print一条数据库中的数据,可以指定显示任何的值: class Question(models.Model):#定义一个数据库表,其中包含question_id和question_text#....def __str__(self): #只想显示question_text return self.question_text 注:在python3.x中str被废弃,使用unicode 5、__init__对象函数__init__比较常见,是对象的初始化函数,例子如下:
6、__new__对象函数__new__()函数是类创建对象时调用的内置函数,必须返回一个生成的对象,__new__()函数在__init__()函数之前执行。一般来说没有比较重载这个函数,除非需要更改new对象的流程,有一种场景“单例模式”要求只能存在一个class A的对象,如果重复创建,那么返回的已经创建过的对象的引用。可以这样使用__new__函数: a.pyclass A(object): def __new__(cls): if not '_instance' in vars(cls): cls._instance=super(A,cls).__new__(cls) return cls._instancea=A()b=A()print id(a)==id(b)out>>True 可以看出,a和b其实引用了同一个对象 7、__class__对象变量instance.__class__表示这个对象的类对象,我们知道在python中,类也是一个对象(好理解么),例:
可以看出,a是A类的一个对象,a.__class__就是A类,将这个类赋值给B,使用B()又可以创建出一个对象b,这个对象b也是A类的对象,(晕了么?),这个__class__有什么卵用呢?下面的例子就可以用到 8、__add__对象函数这其实是一类函数,包括__sub__,__mul__,__mod__,__pow__,__xor__,这些函数都是对加、减、乘、除、乘方、异或、等运算的重载,是我们自定义的对象可以具备运算功能: #a.pyclass A(object): def __init__(self,v): self.v=v def __add__(self,other): #创建创建一个新的对象 x=self.__class__(self.v+2*other.v) return xa=A(1)b=A(2)c=a+bprint c.vouot>>5 这样我们就定义了一个加法操作1+2=1+2*2=5 9、__doc__文档字符串python建议在定义一个类、模块、函数的时候定义一段说明文字,例子如下:
调用别的模块、函数的时候如果不清楚使用方法,也可以直接查看doc文档字符串 10、__iter__和next函数凡是可以被for....in的循环调用的对象,我们称之为可以被迭代的对象,list,str,tuple都可以被迭代,它们都实现了内部的迭代器函数,比如说list,tuple,字符串这些数据结构的迭代器如下: a=[1,2,3,4]b=('i',1,[1,2,3])print a.__iter__()print b.__iter__()out>><listiterator object at 0x0000000001CC7C50>out>><tupleiterator object at 0x0000000001CC7B00> 如果我们要实现一个我们自己的迭代器对象,那么我们必须实现两个默认的方法:__iter__和next。 __iter__()函数将会返回一个迭代器对象,next()函数每次被调用都返回一个值,如果迭代完毕,则raise一个StopIteration的错误,用来终止迭代。下面的例子将实现一个可以迭代的对象,输出a~z的26个字母,该对象接收一个int参数用来表示输出字母的数量,如果该参数超过字母表的长度,则循环从‘a-z’再次进行循环输出:
11、__dict__、__slot__和__all__这三个变量有一些关系,__dict__在类和对象中都存在,它是一个包含变量名和变量的字典,见以下的例子: #a.pyclass A(object): c=3 d=4 def __init__(self): self.a=1 self.b=2 def func(self): passprint A().__dict__print A.__dict__out>>{'a': 1, 'b': 2}out>>{'__module__': '__main__', 'd': 4, 'c': 3, 'func': <function func at 0x00000000021F2BA8>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None, '__init__': <function __init__ at 0x00000000021F2AC8>} 一个对象的__dict__只包含self定义的变量,而一个类的__dict__包含了类里面的函数(func函数)、类变量,以及很多隐性的变量,包括__dict__变量本身也是隐性的。 __slot__变量的用法理解起来比较要难一点,正常的情况下,我们实例化一个对象,可以给这个对象增加任意的成员变量,即使不在类里面定义的变量都可以,如下:
如果我们想限制一下对象绑定的变量,我们可以在类定义的时候增加一个slots变量,这个变量是一个字符串的元组,例子如下: class A(object): __slots__=('a','b','x') def __init__(self): self.a=1 self.b=2 pass #__slots__=('a','b',) def func(self): passa=A()a.x=1#执行到a.y时会报错:AttributeError: 'A' object has no attribute 'y'a.y=lambda x,y:x*yprint a.y(3,5) __all__变量是一个字符串列表,它定义了每一个模块会被from module_name import *这样的语句可以被import的内容(变量,类,函数)
如果在a.py中定义__all__=['A','c'],则B函数对于b.py来说是不可见的。 12、__hash__哈希函数,在python中的对象有一个hashable(可哈希)的概念,对于数字、字符串、元组来说,是不可变的,也就是可哈希的,因此这些对象也可以作为字典的key值。另外,列表、字典等,是可变对象,因此也就是不可哈希的,也就不能作为字典的key值。是否可哈希,可以调用内置函数hash()进行计算,hash()函数返回计算的到的hash值。
当然一般来说,我们不会去重新定义一个对象的__hash__函数,除非我们想实现一个自定义的需求,在stackoverflow有人提出这样一个需求,需要判断有相同词频的字符串是相等的,也就是说“abb”和“bab”这样的字符串是相等的,这个时候我们可以继承字符串类,然后重写哈希函数,如下: import collectionsclass FrequencyString(str): @property def normalized(self): try: return self._normalized except AttributeError: self._normalized = normalized = ''.join(sorted(collections.Counter(self).elements())) return normalized def __eq__(self, other): return self.normalized == other.normalized def __hash__(self): return hash(self.normalized) 13、__getattr__和__setattr__,__delattr__对象函数先介绍两个内置函数,getattr()和setattr(),使用这两个函数可以获取对象的属性,或者给对象的属性赋值:
其实使用这两个函数和直接访问a.a,a.b没有任何区别,但好处是setattr和getattr接受两个字符串去确定访问对象a的哪一个属性,和__import__一样,可以在运行时在决定去访问对象变量的名字,在实际工作中经常会使用这两个函数。 __getattr__()这个函数是在访问对象不存在的成员变量是才会访问的,见下面的例子: class A(object): def __init__(self): self.a=1 self.b=2 def func(self): pass def __getattr__(self,name): print 'getattr' return self.aa=A()print a.dout>>getattrout>>1 在调用a.d时,d不是a的成员变量,则python会去查找对象是否存在__getattr__()函数,如果存在,则返回__getattr__()函数的返回值,我们这里返回的是self.a的值1。 由于__getattr__()的特性,我们可以将__getattr__()设计成一个公共的接口函数,在autotest的proxy.py中就看到了这样的用法:
__setattr__和__getattr__不一样,对象的所有属性赋值,都会经过__setattr__()函数,看下面的例子: class A(object): def __init__(self): self.a=1 self.b=2 def func(self): pass def __getattr__(self,name): print 'getattr' return self.a def __setattr__(self, name, value): print 'setattr %s' % name if name == 'f': return object.__setattr__(self,name,value+1000) else: return object.__setattr__(self, name, value)a=A()a.f=1000print a.fout>>setattr aout>>setattr bout>>setattr fout>>2000 从输出可以看到init函数的self.a和self.b的赋值也经过了__setattr__,而且在赋值的时候我们自定义了一个if逻辑,如果name是‘f’,那么value会增加1000,最终的a.f是2000 __delattr__不举例了,删除一个对象属性用的。 14、__call__对象函数如果一个对象实现了__call__()函数,那么这个对象可以认为是一个函数对象,可以使用加括号的方法来调用,见下面例子:
在实际工作中__call__函数非常有用,可以把一个对象变成callable的对象 |
|
来自: 遥想呼应 > 《Python编程》