前言

先扯一点背景知识

PEP8(Python Enhancement Proposal)是一份python的编码规范,链接:http://www.python.org/dev/peps/pep-0008/

在这份编码规范中的“命名规范-命名风格”这一节的最后,提到了对几种使用前置和后置下划线的,对变量的比较特殊的命名方式:

  • 单下划线开头:弱内部使用标识,无法被from M import *所引用
  • 单下划线结尾:避免和python关键字冲突,可以加个后置下划线
  • 双下划线开头:类成员变量中的私有变量,
  • 双下划线开头,双下划线结尾:这是magic对象或属性的名字,永远不要将这样的命名方式应用于自己的变量和函数

本文主要关注正是以上第四种--python自动在用户命名空间创建的magic变量

1、__name__变量

__name__属性是直接内置在.py文件中的。

  • 如果直接执行.py文件,__name__将被设置为__main__。
  • 如果.py文件是被import,__name__将被设置为.py文件的名字

这个属性经常用来当做一个使用模式的标识:

#a.py
print 'a function'
if __name__=='__main__':
print 'a test'
------------------------------
#b.py
import 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脚本的绝对路径:

#a.py
import os
print os.path.realpath(__file__)
out>>E:\Eclipse_workspace\python_learn\a.py

3、__import__函数

python导入模块时,一般使用import,而import语句其实也是调用builtin函数:__import__()实现的导入,直接使用__import__比较少见,除非导入的模块是不确定的,需要在运行时才能确定导入哪些模块,可以使用__import__,默认接收需要导入的模块名的字符串:

#a.py
def f1():
print 'f1'
def f2():
print 'f2'
#b.py
model=__import__('a')
model.f1()
model.f2()

在memfs的测试中,我的每一个测试case就是一个独立的.py文件,在确定需要测试哪些case后,在运行时才‘动态的’去import相应的case,就是通过__import__来实现的。

4、__str__函数

__str__是一个比较常用的内置函数,在定义类的时候经常使用,__str__函数返回一个字符串,这个字符串就是此对象被print时显示的内容,(如果不定义这个函数,将会显示默认的格式:<__main__.A object at 0x0000000001FB7C50>):

#a.py
import datetime
import os
class A(object):
def __str__(self):
#返回当前的日期
return str(datetime.datetime.now())
a=A()
print a
time.sleep(1)
#每次打印A()的对象,都返回当前的时间
print a
out>>2015-06-25 15:01:01.573000
out>>2015-06-25 15:01:02.573000

这个函数在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__比较常见,是对象的初始化函数,例子如下:

#a.py
class A(object):
pass
class B(A):
#B类继承自A,如果要重写__init__,需要先调用父类的__init__
def __init__(self,*args):
super(B,self).__init__(*args)

6、__new__对象函数

__new__()函数是类创建对象时调用的内置函数,必须返回一个生成的对象,__new__()函数在__init__()函数之前执行。一般来说没有比较重载这个函数,除非需要更改new对象的流程,有一种场景“单例模式”要求只能存在一个class A的对象,如果重复创建,那么返回的已经创建过的对象的引用。可以这样使用__new__函数:

a.py
class A(object):
def __new__(cls):
if not "_instance" in vars(cls):
cls._instance=super(A,cls).__new__(cls)
return cls._instance
a=A()
b=A()
print id(a)==id(b)
out>>True

可以看出,a和b其实引用了同一个对象

7、__class__对象变量

instance.__class__表示这个对象的类对象,我们知道在python中,类也是一个对象(好理解么),例:

#a.py
class A(object):
pass
a=A()
B=a.__class__
b=B()
print type(b)
out>><class '__main__.A'>

可以看出,a是A类的一个对象,a.__class__就是A类,将这个类赋值给B,使用B()又可以创建出一个对象b,这个对象b也是A类的对象,(晕了么?),这个__class__有什么卵用呢?下面的例子就可以用到

8、__add__对象函数

这其实是一类函数,包括__sub__,__mul__,__mod__,__pow__,__xor__,这些函数都是对加、减、乘、除、乘方、异或、等运算的重载,是我们自定义的对象可以具备运算功能:

#a.py
class A(object):
def __init__(self,v):
self.v=v
def __add__(self,other):
#创建创建一个新的对象
x=self.__class__(self.v+2*other.v)
return x
a=A(1)
b=A(2)
c=a+b
print c.v
ouot>>5

这样我们就定义了一个加法操作1+2=1+2*2=5

9、__doc__文档字符串

python建议在定义一个类、模块、函数的时候定义一段说明文字,例子如下:

#c.py
"""
script c's doc
"""
class A(object):
"""
class A's doc
"""
pass
def B():
"""
function B's doc
"""
pass
print __doc__
print A.__doc__
print B.__doc__
out>>script c's doc
out>>class A's doc
out>>function B's doc

调用别的模块、函数的时候如果不清楚使用方法,也可以直接查看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’再次进行循环输出:

import random
class A(object):
def __init__(self,n):
self.stop=n
self.value=0
#字母列表
self.alph=[chr(i) for i in range(97,123)]
def __iter__(self):
return self def next(self):
#如果超过长度超过26则重置
if self.value==len(self.alph):
self.value=0
self.stop=self.stop-len(self.alph)
#最终,已完成n个字符的输出,则结束迭代
if self.value>self.stop:
raise StopIteration
x=self.alph[self.value]
self.value+=1
return x for i in A(1000):
print i,
out>>a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k .....

11、__dict__、__slot__和__all__

这三个变量有一些关系,__dict__在类和对象中都存在,它是一个包含变量名和变量的字典,见以下的例子:

#a.py
class A(object):
c=3
d=4
def __init__(self):
self.a=1
self.b=2
def func(self):
pass
print 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__变量的用法理解起来比较要难一点,正常的情况下,我们实例化一个对象,可以给这个对象增加任意的成员变量,即使不在类里面定义的变量都可以,如下:

#a.py
class A(object): def __init__(self):
self.a=1
self.b=2 a=A()
#给a增加一个x变量
a.x=1
#也可以给a增加一个匿名函数
a.y=lambda x,y:x*y
print a.x
print a.y(3,5)
out>>1
out>>15

如果我们想限制一下对象绑定的变量,我们可以在类定义的时候增加一个slots变量,这个变量是一个字符串的元组,例子如下:

class A(object):
__slots__=('a','b','x')
def __init__(self):
self.a=1
self.b=2 pass
#__slots__=('a','b',)
def func(self):
pass
a=A()
a.x=1
#执行到a.y时会报错:AttributeError: 'A' object has no attribute 'y'
a.y=lambda x,y:x*y
print a.y(3,5)

__all__变量是一个字符串列表,它定义了每一个模块会被from module_name import *这样的语句可以被import的内容(变量,类,函数)

#a.py 不定义__all__
class A(object):
def __init__(self):
self.a=1
self.b=2 def func(self):
pass
def B():
pass c=10 #b.py
from a import *
print A
print B
print c
out>><class 'learn_draft.A'>
out>><function B at 0x00000000021D1438>
out>>10

如果在a.py中定义__all__=['A','c'],则B函数对于b.py来说是不可见的。

12、__hash__

哈希函数,在python中的对象有一个hashable(可哈希)的概念,对于数字、字符串、元组来说,是不可变的,也就是可哈希的,因此这些对象也可以作为字典的key值。另外,列表、字典等,是可变对象,因此也就是不可哈希的,也就不能作为字典的key值。是否可哈希,可以调用内置函数hash()进行计算,hash()函数返回计算的到的hash值。

  • 完全相同的变量,调用哈希算法的到的hash值一定是相同的

当然一般来说,我们不会去重新定义一个对象的__hash__函数,除非我们想实现一个自定义的需求,在stackoverflow有人提出这样一个需求,需要判断有相同词频的字符串是相等的,也就是说“abb”和“bab”这样的字符串是相等的,这个时候我们可以继承字符串类,然后重写哈希函数,如下:

import collections

class 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.py
class A(object):
def __init__(self):
self.a=1
self.b=2
a=A()
setattr(a,'a',3)
print a.a
print getattr(a,'b')
out>>3
out>>2

其实使用这两个函数和直接访问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.a a=A()
print a.d
out>>getattr
out>>1

在调用a.d时,d不是a的成员变量,则python会去查找对象是否存在__getattr__()函数,如果存在,则返回__getattr__()函数的返回值,我们这里返回的是self.a的值1。

由于__getattr__()的特性,我们可以将__getattr__()设计成一个公共的接口函数,在autotest的proxy.py中就看到了这样的用法:

class ServiceProxy(object):

def __init__(self, serviceURL, serviceName=None, headers=None):
self.__serviceURL = serviceURL
self.__serviceName = serviceName
self.__headers = headers or {} def __getattr__(self, name):
if self.__serviceName is not None:
name = "%s.%s" % (self.__serviceName, name)
return ServiceProxy(self.__serviceURL, name, self.__headers) #调用的时候,op是执行的特定操作的字符串,op传入__getattr__将会把ServiceProxy对象重新的内部变量重新赋值,然后返回一个更新之后的对象
function = getattr(self.proxy, op)

__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=1000
print a.f
out>>setattr a
out>>setattr b
out>>setattr f
out>>2000

从输出可以看到init函数的self.a和self.b的赋值也经过了__setattr__,而且在赋值的时候我们自定义了一个if逻辑,如果name是‘f’,那么value会增加1000,最终的a.f是2000

__delattr__不举例了,删除一个对象属性用的。

14、__call__对象函数

如果一个对象实现了__call__()函数,那么这个对象可以认为是一个函数对象,可以使用加括号的方法来调用,见下面例子:

class A(object):
def __init__(self):
self.li=['a','b','c','d']
def func(self):
pass
def __call__(self,n):
#返回li列表的第n个元素
return self.li[n] a=A()
#a可以当做函数一样调用
print a(0),a(1),a(2)
out>>a b c

在实际工作中__call__函数非常有用,可以把一个对象变成callable的对象

最新文章

  1. Codeforces Round #353 (Div. 2) ABCDE 题解 python
  2. spring3.0使用annotation完全代替XML(三)
  3. 判断横屏竖屏,然后CSS重新计算
  4. MySQL基础 - 内置函数
  5. Android IOS WebRTC 音视频开发总结(二十)-- 自由职业
  6. MFC ListControl用法
  7. angularjs 实现排序功能
  8. 自己写的angularJs排序指令【原创】
  9. 厉害了我的雅虎!卖掉主业后更名为阿里他爸(Altaba)
  10. HTTPS加密流程超详解(一)前期准备
  11. Vue初学跳坑
  12. CF396C On Changing Tree
  13. 隔离 docker 容器中的用户
  14. Spring——事务
  15. 修改教材P74 一行代码 NineNineTable.java, 让执行结果是个三角形
  16. 用jquery得到select选中的值
  17. Java中的代理机制
  18. AJAX的优点 个人理解记录
  19. stderr 和stdout
  20. CentOS7下 将django工程部署到Apache2.4上

热门文章

  1. 【HDU 2089】 不要62
  2. 聊聊Spring中的工厂
  3. struct 结构体解析(原)
  4. Com组件介绍
  5. oracle 分库分表(sharding)
  6. NOI前总结:点分治
  7. zz MBR,EBR
  8. html中target的用法
  9. E20170404-gg
  10. POJ2488【DFS】