python笔记_magic变量和函数
前言
先扯一点背景知识
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的对象
python笔记_magic变量和函数的更多相关文章
- 【python笔记】Qt+云函数 实现简单的登录框制作
[python笔记]Qt+云函数 实现简单的登录框制作 备注:前置条件:QtDesigner.pycharm.PyQt5.配置好的云函数(百度的叫函数计算CFC,用来充当一个简陋的服务器,主要是免费) ...
- Python笔记(2)函数
python中一切皆对象,函数也看做对象.函数被函数名所引用,但是同样的他也可以被其他标识符所引用,可以作为参数传递. def f(): return "hi" 可见a引用了函数返 ...
- A Byte of Python 笔记(5)函数:定义、形参、局部变量、默认参数、关键参数
第7章 函数 函数是重要的程序段.它们允许你给一块语句一个名称,然后你可以在程序的任何地方使用这个名称任意多次地运行这个语句块.这被称为 调用 函数. 定义函数 函数通过 def 关键字定义.def ...
- python笔记1,语法,函数,类和实例,异常
>>> int(12.34) 12 >>> float('12.34') 12.34 >>> str(1.23) '1.23' >>& ...
- python笔记4 内置函数,匿名函数.递归函数 面向对象(基础, 组合,继承)
内置函数 eval和exec eval :执行字符串中的代码并将结果返回给执行者,有返回值 exec:执行字符串中的代码,往往用于执行流程语句,没有返回值. s1 = '1+2' s2 = 'prin ...
- Python笔记·第十一章—— 函数 (二) 装饰器
一 为何要用装饰器 有的时候写完一段代码,过段时间需要对它进行升级.添加一些新功能,但是如果要直接修改原来的代码会影响其他人的调用,所以就需要一个不修改源代码且不修改原函数的调用方式的东西又能为原函数 ...
- Python笔记(十一)_匿名函数与map()、filter()
匿名函数 无需显式定义函数名,和函数过程,使代码更精简的lambda表达式 函数没有命名,不用担心函数名的冲突 冒号前面代表函数的参数,后面表示计算过程 >>>func=lambda ...
- 第二周Python笔记之 变量的三元运算
如果变量a小于b,则d的值取a变量的值,否则取c变量的值
- python文件之间变量和函数的 获取/调用 的方法
随机推荐
- Struts2的各种标签库
1 在JSP中使用taglib编译指令导入标签库 <%@ taglib prefix="s" uri="/struts-tags" %> ----- ...
- U3D Navigation
让我们来一起粗步认识一下NavMesh的简单使用 首先我们建立一个新场景,在新场景我们创建 一个地形或者创建一个Plane, 然后在其上面用Cube或者其它的建立一些障碍物 再创建自己需要为其设置自动 ...
- 【转】maven的安装、配置以及下载jar包
原文地址:https://blog.csdn.net/qq_40673345/article/details/79015456 1.下载maven的压缩包,并解压到除了C盘里的maven文件夹中 2. ...
- Babel转码器
Babel是一个广泛使用的ES6转码器,可以将ES6代码转为ES5代码,从而在现有环境执行.这意味着,你可以用ES6的方法 编写程序,又不用担心现在环境是否支持.
- UIAlterController 的使用
相对于IOS8.4之后苹果对提示框做了进一步的封装,这将与之前的提示框有很大的同. 之前的 UIAlterView 是弹出一个提示框. 而今天学习的提示框是 通过视图控制器进行弹出,这就意味着,我们 ...
- 父窗口与iFrame之间调用方法和元素
父窗口与iFrame之间调用方法和元素 父窗口调用子窗口: 调用元素 js格式: var obj=document.getElementById("iframe的name").co ...
- 使用VirtualBox虚拟机搭建局域网(续)
最近家中Windows 10更新后抽风,最后不得不重装系统,原本配置好的开发环境全部要重装,也包括局域网搭建,但由于是家庭网络并非公司局域网,故旧文<使用VirtualBox虚拟机搭建局域网&g ...
- 2.25-2.26 MapReduce执行流程Shuffle讲解
原文链接:https://langyu.iteye.com/blog/992916 Shuffle过程是MapReduce的核心,也被称为奇迹发生的地方.要想理解MapReduce, Shuffle是 ...
- 038--HTML
一.HTML的定义 1. 超文本标记语言(Hypertext Markup Language,HTML)通过标签语言来标记要显示的网页中的各个部分.一套规则,浏览器认识的规则 2. 浏览器按顺序渲染网 ...
- P4692 [Ynoi2016]谁的梦
传送门 分别考虑每一种颜色对答案的贡献.每种颜色的贡献就是他出现的区间个数,那么可以用总区间减去不包含它的区间个数,把每一个序列里不包含它的区间个数加起来,然后不同序列用乘法原理计算即可 于是我辛辛苦 ...