经常使用模块二html
模块使用python
内置函数mysql
反射程序员
经常使用模块二 面试
hashlib模块算法
Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等。sql
什么是摘要算法呢?摘要算法又称哈希算法、散列算法。它经过一个函数,把任意长度的数据转换为一个长度固定的数据串(一般用16进制的字符串表示)。shell
摘要算法就是经过摘要函数f()对任意长度的数据data计算出固定长度的摘要digest,目的是为了发现原始数据是否被人篡改过。数据库
摘要算法之因此能指出数据是否被篡改过,就是由于摘要函数是一个单向函数,计算f(data)很容易,但经过digest反推data却很是困难。并且,对原始数据作一个bit的修改,都会致使计算出的摘要彻底不一样。windows
咱们以常见的摘要算法MD5为例,计算出一个字符串的MD5值:
import hashlib md5 = hashlib.md5() md5.update('how to use md5 in python hashlib?') print md5.hexdigest() 计算结果以下: d26a53750bc40b38b65a520292f69306
若是数据量很大,能够分块屡次调用update(),最后计算的结果是同样的:
md5 = hashlib.md5() md5.update('how to use md5 in ') md5.update('python hashlib?') print md5.hexdigest()
MD5是最多见的摘要算法,速度很快,生成结果是固定的128 bit字节,一般用一个32位的16进制字符串表示。另外一种常见的摘要算法是SHA1,调用SHA1和调用MD5彻底相似:
import hashlib sha1 = hashlib.sha1() sha1.update('how to use sha1 in ') sha1.update('python hashlib?') print sha1.hexdigest()
SHA1的结果是160 bit字节,一般用一个40位的16进制字符串表示。比SHA1更安全的算法是SHA256和SHA512,不过越安全的算法越慢,并且摘要长度更长。
任何容许用户登陆的网站都会存储用户登陆的用户名和口令。如何存储用户名和口令呢?方法是存到数据库表中:
name | password --------+---------- michael | 123456 bob | abc999 alice | alice2008
若是以明文保存用户口令,若是数据库泄露,全部用户的口令就落入黑客的手里。此外,网站运维人员是能够访问数据库的,也就是能获取到全部用户的口令。正确的保存口令的方式是不存储用户的明文口令,而是存储用户口令的摘要,好比MD5:
username | password ---------+--------------------------------- michael | e10adc3949ba59abbe56e057f20f883e bob | 878ef96e86145580c38c87f0410ad153 alice | 99b1c2188db85afee403b1536010c2c9
考虑这么个状况,不少用户喜欢用123456,888888,password这些简单的口令,因而,黑客能够事先计算出这些经常使用口令的MD5值,获得一个反推表:
'e10adc3949ba59abbe56e057f20f883e': '123456' '21218cca77804d2ba1922c33e0151105': '888888' '5f4dcc3b5aa765d61d8327deb882cf99': 'password'
这样,无需破解,只须要对比数据库的MD5,黑客就得到了使用经常使用口令的用户帐号。
对于用户来说,固然不要使用过于简单的口令。可是,咱们可否在程序设计上对简单口令增强保护呢?
因为经常使用口令的MD5值很容易被计算出来,因此,要确保存储的用户口令不是那些已经被计算出来的经常使用口令的MD5,这一方法经过对原始口令加一个复杂字符串来实现,俗称“加盐”:
hashlib.md5("salt".encode("utf8"))
通过Salt处理的MD5口令,只要Salt不被黑客知道,即便用户输入简单口令,也很难经过MD5反推明文口令。
可是若是有两个用户都使用了相同的简单口令好比123456,在数据库中,将存储两条相同的MD5值,这说明这两个用户的口令是同样的。有没有办法让使用相同口令的用户存储不一样的MD5呢?
若是假定用户没法修改登陆名,就能够经过把登陆名做为Salt的一部分来计算MD5,从而实现相同口令的用户也存储不一样的MD5。
摘要算法在不少地方都有普遍的应用。要注意摘要算法不是加密算法,不能用于加密(由于没法经过摘要反推明文),只能用于防篡改,可是它的单向计算特性决定了能够在不存储明文口令的状况下验证用户口令。
configparser模块
该模块适用于配置文件的格式与windows ini文件相似,能够包含一个或多个节(section),每一个节能够有多个参数(键=值)。
来看一个好多软件的常见文档格式以下:
[DEFAULT] ServerAliveInterval = 45 Compression = yes CompressionLevel = 9 ForwardX11 = yes [bitbucket.org] User = hg [topsecret.server.com] Port = 50022 ForwardX11 = no
若是想用python生成一个这样的文档怎么作呢?
import configparser config = configparser.ConfigParser() config["DEFAULT"] = {'ServerAliveInterval': '45', 'Compression': 'yes', 'CompressionLevel': '9', 'ForwardX11':'yes' } config['bitbucket.org'] = {'User':'hg'} config['topsecret.server.com'] = {'Host Port':'50022','ForwardX11':'no'} with open('example.ini', 'w') as configfile: config.write(configfile)
import configparser config = configparser.ConfigParser() #---------------------------查找文件内容,基于字典的形式 print(config.sections()) # [] config.read('example.ini') print(config.sections()) # ['bitbucket.org', 'topsecret.server.com'] print('bytebong.com' in config) # False print('bitbucket.org' in config) # True print(config['bitbucket.org']["user"]) # hg print(config['DEFAULT']['Compression']) #yes print(config['topsecret.server.com']['ForwardX11']) #no print(config['bitbucket.org']) #<Section: bitbucket.org> for key in config['bitbucket.org']: # 注意,有default会默认default的键 print(key) print(config.options('bitbucket.org')) # 同for循环,找到'bitbucket.org'下全部键 print(config.items('bitbucket.org')) #找到'bitbucket.org'下全部键值对 print(config.get('bitbucket.org','compression')) # yes get方法Section下的key对应的value
import configparser config = configparser.ConfigParser() config.read('example.ini') config.add_section('yuan') config.remove_section('bitbucket.org') config.remove_option('topsecret.server.com',"forwardx11") config.set('topsecret.server.com','k1','11111') config.set('yuan','k2','22222') config.write(open('new2.ini', "w"))
logging模块
import logging logging.debug('debug message') logging.info('info message') logging.warning('warning message') logging.error('error message') logging.critical('critical message')
默认状况下Python的logging模块将日志打印到了标准输出中,且只显示了大于等于WARNING级别的日志,这说明默认的日志级别设置为WARNING(日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG),默认的日志格式为日志级别:Logger名称:用户输出消息。
灵活配置日志级别,日志格式,输出位置:
import logging logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', datefmt='%a, %d %b %Y %H:%M:%S', filename='/tmp/test.log', filemode='w') logging.debug('debug message') logging.info('info message') logging.warning('warning message') logging.error('error message') logging.critical('critical message')
配置参数:
logging.basicConfig()函数中可经过具体参数来更改logging模块默认行为,可用参数有: filename:用指定的文件名建立FiledHandler,这样日志会被存储在指定的文件中。 filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。 format:指定handler使用的日志显示格式。 datefmt:指定日期时间格式。 level:设置rootlogger(后边会讲解具体概念)的日志级别 stream:用指定的stream建立StreamHandler。能够指定输出到sys.stderr,sys.stdout或者文件(f=open(‘test.log’,’w’)),默认为sys.stderr。若同时列出了filename和stream两个参数,则stream参数会被忽略。 format参数中可能用到的格式化串: %(name)s Logger的名字 %(levelno)s 数字形式的日志级别 %(levelname)s 文本形式的日志级别 %(pathname)s 调用日志输出函数的模块的完整路径名,可能没有 %(filename)s 调用日志输出函数的模块的文件名 %(module)s 调用日志输出函数的模块名 %(funcName)s 调用日志输出函数的函数名 %(lineno)d 调用日志输出函数的语句所在的代码行 %(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示 %(relativeCreated)d 输出日志信息时的,自Logger建立以 来的毫秒数 %(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒 %(thread)d 线程ID。可能没有 %(threadName)s 线程名。可能没有 %(process)d 进程ID。可能没有 %(message)s用户输出的消息
import logging logger = logging.getLogger() # 建立一个handler,用于写入日志文件 fh = logging.FileHandler('test.log',encoding='utf-8')
# 再建立一个handler,用于输出到控制台
ch = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter)
ch.setFormatter(formatter)
logger.addHandler(fh) #logger对象能够添加多个fh和ch对象
logger.addHandler(ch)
logger.debug('logger debug message')
logger.info('logger info message')
logger.warning('logger warning message')
logger.error('logger error message')
logger.critical('logger critical message')
logging库提供了多个组件:Logger、Handler、Filter、Formatter。Logger对象提供应用程序可直接使用的接口,Handler发送日志到适当的目的地,Filter提供了过滤日志信息的方法,Formatter指定日志显示格式。另外,能够经过:logger.setLevel(logging.Debug)设置级别,固然,也能够经过
fh.setLevel(logging.Debug)单对文件流设置某个级别。
常见的场景:一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀。
但其实import加载的模块分为四个通用类别:
1 使用python编写的代码(.py文件)
2 已被编译为共享库或DLL的C或C++扩展
3 包好一组模块的包
4 使用C编写并连接到python解释器的内置模块
若是你退出python解释器而后从新进入,那么你以前定义的函数或者变量都将丢失,所以咱们一般将程序写到文件中以便永久保存下来,须要时就经过python test.py方式去执行,此时test.py被称为脚本script。
随着程序的发展,功能愈来愈多,为了方便管理,咱们一般将程序分红一个个的文件,这样作程序的结构更清晰,方便管理。这时咱们不只仅能够把这些文件当作脚本去执行,还能够把他们当作模块来导入到其余的模块中,实现了功能的重复利用,
示例文件:自定义模块my_module.py,文件名my_module.py,模块名my_module
#my_module.py print('from the my_module.py') money=1000 def read1(): print('my_module->read1->money',money) def read2(): print('my_module->read2 calling read1') read1() def change(): global money money=0
模块能够包含可执行的语句和函数的定义,这些语句的目的是初始化模块,它们只在模块名第一次遇到导入import语句时才执行(import语句是能够在程序中的任意位置使用的,且针对同一个模块很import屡次,为了防止你重复导入,python的优化手段是:第一次导入后就将模块名加载到内存了,后续的import语句仅是对已经加载大内存中的模块对象增长了一次引用,不会从新执行模块内的语句),以下
#demo.py import my_module #只在第一次导入时才执行my_module.py内代码,此处的显式效果是只打印一次'from the my_module.py',固然其余的顶级代码也都被执行了,只不过没有显示效果. import my_module import my_module import my_module ''' 执行结果: from the my_module.py '''
咱们能够从sys.modules中找到当前已经加载的模块,sys.modules是一个字典,内部包含模块名与模块对象的映射,该字典决定了导入模块时是否须要从新导入。
每一个模块都是一个独立的名称空间,定义在这个模块中的函数,把这个模块的名称空间当作全局名称空间,这样咱们在编写本身的模块时,就不用担忧咱们定义在本身模块中全局变量会在被导入时,与使用者的全局变量冲突
#测试一:money与my_module.money不冲突 #demo.py import my_module money=10 print(my_module.money) ''' 执行结果: from the my_module.py 1000 '''
#测试二:read1与my_module.read1不冲突 #demo.py import my_module def read1(): print('========') my_module.read1() ''' 执行结果: from the my_module.py my_module->read1->money 1000 '''
#测试三:执行my_module.change()操做的全局变量money仍然是my_module中的 #demo.py import my_module money=1 my_module.change() print(money) ''' 执行结果: from the my_module.py 1 '''
总结:首次导入模块my_module时会作三件事:
1.为源文件(my_module模块)建立新的名称空间,在my_module中定义的函数和方法如果使用到了global时访问的就是这个名称空间。
2.在新建立的命名空间中执行模块中包含的代码,见初始导入import my_module
1 提示:导入模块时到底执行了什么? 2 3 In fact function definitions are also ‘statements’ that are ‘executed’; the execution of a module-level function definition enters the function name in the module’s global symbol table. 4 事实上函数定义也是“被执行”的语句,模块级别函数定义的执行将函数名放入模块全局名称空间表,用globals()能够查看
3.建立名字my_module来引用该命名空间
1 这个名字和变量名没什么区别,都是‘第一类的’,且使用my_module.名字的方式能够访问my_module.py文件中定义的名字,my_module.名字与test.py中的名字来自两个彻底不一样的地方。
为模块名起别名,至关于m1=1;m2=m1
1 import my_module as sm 2 print(sm.money)
示范用法一:
有两中sql模块mysql和oracle,根据用户的输入,选择不一样的sql功能
#mysql.py def sqlparse(): print('from mysql sqlparse') #oracle.py def sqlparse(): print('from oracle sqlparse') #test.py db_type=input('>>: ') if db_type == 'mysql': import mysql as db elif db_type == 'oracle': import oracle as db db.sqlparse()
示范用法二:
为已经导入的模块起别名的方式对编写可扩展的代码颇有用,假设有两个模块xmlreader.py和csvreader.py,它们都定义了函数read_data(filename):用来从文件中读取一些数据,但采用不一样的输入格式。能够编写代码来选择性地挑选读取模块,例如
if file_format == 'xml': import xmlreader as reader elif file_format == 'csv': import csvreader as reader data=reader.read_date(filename)
在一行导入多个模块
1 import sys,os,re
对比import my_module,会将源文件的名称空间'my_module'带到当前名称空间中,使用时必须是my_module.名字的方式
而from 语句至关于import,也会建立新的名称空间,可是将my_module中的名字直接导入到当前的名称空间中,在当前名称空间中,直接使用名字就能够了、
1 from my_module import read1,read2
这样在当前位置直接使用read1和read2就行了,执行时,仍然以my_module.py文件全局名称空间
#测试一:导入的函数read1,执行时仍然回到my_module.py中寻找全局变量money #demo.py from my_module import read1 money=1000 read1() ''' 执行结果: from the my_module.py spam->read1->money 1000 ''' #测试二:导入的函数read2,执行时须要调用read1(),仍然回到my_module.py中找read1() #demo.py from my_module import read2 def read1(): print('==========') read2() ''' 执行结果: from the my_module.py my_module->read2 calling read1 my_module->read1->money 1000 '''
若是当前有重名read1或者read2,那么会有覆盖效果。
#测试三:导入的函数read1,被当前位置定义的read1覆盖掉了 #demo.py from my_module import read1 def read1(): print('==========') read1() ''' 执行结果: from the my_module.py ========== '''
须要特别强调的一点是:python中的变量赋值不是一种存储操做,而只是一种绑定关系,以下:
from my_module import money,read1 money=100 #将当前位置的名字money绑定到了100 print(money) #打印当前的名字 read1() #读取my_module.py中的名字money,仍然为1000 ''' from the my_module.py 100 my_module->read1->money 1000 '''
也支持as
1 from my_module import read1 as read
也支持导入多行
1 from my_module import (read1, 2 read2, 3 money)
from my_module import * 把my_module中全部的不是如下划线(_)开头的名字都导入到当前位置,大部分状况下咱们的python程序不该该使用这种导入方式,由于*你不知道你导入什么名字,颇有可能会覆盖掉你以前已经定义的名字。并且可读性极其的差,在交互式环境中导入时没有问题。
from my_module import * #将模块my_module中全部的名字都导入到当前名称空间 print(money) print(read1) print(read2) print(change) ''' 执行结果: from the my_module.py 1000 <function read1 at 0x1012e8158> <function read2 at 0x1012e81e0> <function change at 0x1012e8268> '''
在my_module.py中新增一行
__all__=['money','read1'] #这样在另一个文件中用from my_module import *就这能导入列表中规定的两个名字
*若是my_module.py中的名字前加_,即_money,则from my_module import *,则_money不能被导入
思考:假若有两个模块a,b。我可不能够在a模块中import b ,再在b模块中import a?
考虑到性能的缘由,每一个模块只被导入一次,放入字典sys.modules中,若是你改变了模块的内容,你必须重启程序,python不支持从新加载或卸载以前导入的模块,
有的同窗可能会想到直接从sys.modules中删除一个模块不就能够卸载了吗,注意了,你删了sys.modules中的模块对象仍然可能被其余程序的组件所引用,于是不会被清除。
特别的对于咱们引用了这个模块中的一个类,用这个类产生了不少对象,于是这些对象都有关于这个模块的引用。
若是只是你想交互测试的一个模块,使用 importlib.reload(), e.g. import importlib; importlib.reload(modulename),这只能用于测试环境。
def func1(): print('func1')
import time,importlib import aa time.sleep(20) # importlib.reload(aa) aa.func1()
在20秒的等待时间里,修改aa.py中func1的内容,等待test.py的结果。
打开importlib注释,从新测试
咱们能够经过模块的全局变量__name__来查看模块名:
当作脚本运行:
__name__ 等于'__main__'
当作模块导入:
__name__= 模块名
做用:用来控制.py文件在不一样的应用场景下执行不一样的逻辑
if __name__ == '__main__':
def fib(n): a, b = 0, 1 while b < n: print(b, end=' ') a, b = b, a+b print() if __name__ == "__main__": print(__name__) num = input('num :') fib(int(num))
python解释器在启动时会自动加载一些模块,可使用sys.modules查看
在第一次导入某个模块时(好比my_module),会先检查该模块是否已经被加载到内存中(当前执行文件的名称空间对应的内存),若是有则直接引用
若是没有,解释器则会查找同名的内建模块,若是尚未找到就从sys.path给出的目录列表中依次寻找my_module.py文件。
因此总结模块的查找顺序是:内存中已经加载的模块->内置模块->sys.path路径中包含的模块
sys.path的初始化的值来自于:
The directory containing the input script (or the current directory when no file is specified).
PYTHONPATH (a list of directory names, with the same syntax as the shell variable PATH).
The installation-dependent default.
须要特别注意的是:咱们自定义的模块名不该该与系统内置模块重名。虽然每次都说,可是仍然会有人不停的犯错。
在初始化后,python程序能够修改sys.path,路径放到前面的优先于标准库被加载。
1 >>> import sys 2 >>> sys.path.append('/a/b/c/d') 3 >>> sys.path.insert(0,'/x/y/z') #排在前的目录,优先被搜索
注意:搜索时按照sys.path中从左到右的顺序查找,位于前的优先被查找,sys.path中还可能包含.zip归档文件和.egg文件,python会把.zip归档文件当成一个目录去处理。
#首先制做归档文件:zip module.zip foo.py bar.py import sys sys.path.append('module.zip') import foo,bar #也可使用zip中目录结构的具体位置 sys.path.append('module.zip/lib/python') #windows下的路径不加r开头,会语法错误 sys.path.insert(0,r'C:\Users\Administrator\PycharmProjects\a')
至于.egg文件是由setuptools建立的包,这是按照第三方python库和扩展时使用的一种常见格式,.egg文件实际上只是添加了额外元数据(如版本号,依赖项等)的.zip文件。
须要强调的一点是:只能从.zip文件中导入.py,.pyc等文件。使用C编写的共享库和扩展块没法直接从.zip文件中加载(此时setuptools等打包系统有时能提供一种规避方法),且从.zip中加载文件不会建立.pyc或者.pyo文件,所以必定要事先建立他们,来避免加载模块是性能降低。
官网解释
#官网连接:https://docs.python.org/3/tutorial/modules.html#the-module-search-path 搜索路径: 当一个命名为my_module的模块被导入时 解释器首先会从内建模块中寻找该名字 找不到,则去sys.path中找该名字 sys.path从如下位置初始化 执行文件所在的当前目录 PTYHONPATH(包含一系列目录名,与shell变量PATH语法同样) 依赖安装时默认指定的 注意:在支持软链接的文件系统中,执行脚本所在的目录是在软链接以后被计算的,换句话说,包含软链接的目录不会被添加到模块的搜索路径中 在初始化后,咱们也能够在python程序中修改sys.path,执行文件所在的路径默认是sys.path的第一个目录,在全部标准库路径的前面。这意味着,当前目录是优先于标准库目录的,须要强调的是:咱们自定义的模块名不要跟python标准库的模块名重复,除非你是故意的,傻叉。
为了提升加载模块的速度,强调强调强调:提升的是加载速度而绝非运行速度。python解释器会在__pycache__目录中下缓存每一个模块编译后的版本,格式为:module.version.pyc。一般会包含python的版本号。例如,在CPython3.3版本下,my_module.py模块会被缓存成__pycache__/my_module.cpython-33.pyc。这种命名规范保证了编译后的结果多版本共存。
Python检查源文件的修改时间与编译的版本进行对比,若是过时就须要从新编译。这是彻底自动的过程。而且编译的模块是平台独立的,因此相同的库能够在不一样的架构的系统之间共享,即pyc使一种跨平台的字节码,相似于JAVA火.NET,是由python虚拟机来执行的,可是pyc的内容跟python的版本相关,不一样的版本编译后的pyc文件不一样,2.5编译的pyc文件不能到3.5上执行,而且pyc文件是能够反编译的,于是它的出现仅仅是用来提高模块的加载速度的。
python解释器在如下两种状况下不检测缓存
1 若是是在命令行中被直接导入模块,则按照这种方式,每次导入都会从新编译,而且不会存储编译后的结果(python3.3之前的版本应该是这样)
python -m my_module.py
2 若是源文件不存在,那么缓存的结果也不会被使用,若是想在没有源文件的状况下来使用编译后的结果,则编译后的结果必须在源目录下
提示:
1.模块名区分大小写,foo.py与FOO.py表明的是两个模块
2.你可使用-O或者-OO转换python命令来减小编译模块的大小
-O转换会帮你去掉assert语句 -OO转换会帮你去掉assert语句和__doc__文档字符串 因为一些程序可能依赖于assert语句或文档字符串,你应该在在确认须要的状况下使用这些选项。
3.在速度上从.pyc文件中读指令来执行不会比从.py文件中读指令执行更快,只有在模块被加载时,.pyc文件才是更快的
4.只有使用import语句是才将文件自动编译为.pyc文件,在命令行或标准输入中指定运行脚本则不会生成这类文件,于是咱们可使用compieall模块为一个目录中的全部模块建立.pyc文件
模块能够做为一个脚本(使用python -m compileall)编译Python源 python -m compileall /module_directory 递归着编译 若是使用python -O -m compileall /module_directory -l则只一层 命令行里使用compile()函数时,自动使用python -O -m compileall 详见:https://docs.python.org/3/library/compileall.html#module-compileall
内建函数dir是用来查找模块中定义的名字,返回一个有序字符串列表
import my_module dir(my_module)
若是没有参数,dir()列举出当前定义的名字
dir()不会列举出内建函数或者变量的名字,它们都被定义到了标准模块builtin中,能够列举出它们,
import builtins dir(builtins)
isinstance(obj,cls)检查是否obj是不是类 cls 的对象
class Foo(object): pass obj = Foo() isinstance(obj, Foo)
issubclass(sub, super)检查sub类是不是 super 类的派生类
class Foo(object): pass class Bar(Foo): pass issubclass(Bar, Foo)
1 什么是反射
反射的概念是由Smith在1982年首次提出的,主要是指程序能够访问、检测和修改它自己状态或行为的一种能力(自省)。这一律念的提出很快引起了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。
2 python面向对象中的反射:经过字符串的形式操做对象相关的属性。python中的一切事物都是对象(均可以使用反射)
四个能够实现自省的函数
下列方法适用于类和对象(一切皆对象,类自己也是一个对象)
def hasattr(*args, **kwargs): # real signature unknown """ Return whether the object has an attribute with the given name. This is done by calling getattr(obj, name) and catching AttributeError. """ pass
def getattr(object, name, default=None): # known special case of getattr """ getattr(object, name[, default]) -> value Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y. When a default argument is given, it is returned when the attribute doesn't exist; without it, an exception is raised in that case. """ pass
def setattr(x, y, v): # real signature unknown; restored from __doc__ """ Sets the named attribute on the given object to the specified value. setattr(x, 'y', v) is equivalent to ``x.y = v'' """ pass
def delattr(x, y): # real signature unknown; restored from __doc__ """ Deletes the named attribute from the given object. delattr(x, 'y') is equivalent to ``del x.y'' """ pass
class Foo: f = '类的静态变量' def __init__(self,name,age): self.name=name self.age=age def say_hi(self): print('hi,%s'%self.name) obj=Foo('egon',73) #检测是否含有某属性 print(hasattr(obj,'name')) print(hasattr(obj,'say_hi')) #获取属性 n=getattr(obj,'name') print(n) func=getattr(obj,'say_hi') func() print(getattr(obj,'aaaaaaaa','不存在啊')) #报错 #设置属性 setattr(obj,'sb',True) setattr(obj,'show_name',lambda self:self.name+'sb') print(obj.__dict__) print(obj.show_name(obj)) #删除属性 delattr(obj,'age') delattr(obj,'show_name') delattr(obj,'show_name111')#不存在,则报错 print(obj.__dict__)
class Foo(object): staticField = "old boy" def __init__(self): self.name = 'wupeiqi' def func(self): return 'func' @staticmethod def bar(): return 'bar' print getattr(Foo, 'staticField') print getattr(Foo, 'func') print getattr(Foo, 'bar')
#!/usr/bin/env python # -*- coding:utf-8 -*- import sys def s1(): print 's1' def s2(): print 's2' this_module = sys.modules[__name__] hasattr(this_module, 's1') getattr(this_module, 's2')
导入其余模块,利用反射查找该模块是否存在某个方法
#!/usr/bin/env python # -*- coding:utf-8 -*- def test(): print('from the test')
#!/usr/bin/env python # -*- coding:utf-8 -*- """ 程序目录: module_test.py index.py 当前文件: index.py """ import module_test as obj #obj.test() print(hasattr(obj,'test')) getattr(obj,'test')()
改变对象的字符串显示__str__,__repr__
自定制格式化字符串__format__
#_*_coding:utf-8_*_ format_dict={ 'nat':'{obj.name}-{obj.addr}-{obj.type}',#学校名-学校地址-学校类型 'tna':'{obj.type}:{obj.name}:{obj.addr}',#学校类型:学校名:学校地址 'tan':'{obj.type}/{obj.addr}/{obj.name}',#学校类型/学校地址/学校名 } class School: def __init__(self,name,addr,type): self.name=name self.addr=addr self.type=type def __repr__(self): return 'School(%s,%s)' %(self.name,self.addr) def __str__(self): return '(%s,%s)' %(self.name,self.addr) def __format__(self, format_spec): # if format_spec if not format_spec or format_spec not in format_dict: format_spec='nat' fmt=format_dict[format_spec] return fmt.format(obj=self) s1=School('oldboy1','北京','私立') print('from repr: ',repr(s1)) print('from str: ',str(s1)) print(s1) ''' str函数或者print函数--->obj.__str__() repr或者交互式解释器--->obj.__repr__() 若是__str__没有被定义,那么就会使用__repr__来代替输出 注意:这俩方法的返回值必须是字符串,不然抛出异常 ''' print(format(s1,'nat')) print(format(s1,'tna')) print(format(s1,'tan')) print(format(s1,'asfdasdffd'))
class B: def __str__(self): return 'str : class B' def __repr__(self): return 'repr : class B' b=B() print('%s'%b) print('%r'%b)
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法通常无须定义,由于Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,由于此工做都是交给Python解释器来执行,因此,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
class Foo: def __del__(self): print('执行我啦') f1=Foo() del f1 print('------->') #输出结果 执行我啦 ------->
class Foo: def __init__(self,name): self.name=name def __getitem__(self, item): print(self.__dict__[item]) def __setitem__(self, key, value): self.__dict__[key]=value def __delitem__(self, key): print('del obj[key]时,我执行') self.__dict__.pop(key) def __delattr__(self, item): print('del obj.key时,我执行') self.__dict__.pop(item) f1=Foo('sb') f1['age']=18 f1['age1']=19 del f1.age1 del f1['age'] f1['name']='alex' print(f1.__dict__)
class Foo: def __init__(self,name): self.name=name def __getitem__(self, item): print(self.__dict__[item]) def __setitem__(self, key, value): self.__dict__[key]=value def __delitem__(self, key): print('del obj[key]时,我执行') self.__dict__.pop(key) def __delattr__(self, item): print('del obj.key时,我执行') self.__dict__.pop(item) f1=Foo('sb') f1['age']=18 f1['age1']=19 del f1.age1 del f1['age'] f1['name']='alex' print(f1.__dict__)
class A: def __init__(self): self.x = 1 print('in init function') def __new__(cls, *args, **kwargs): print('in new function') return object.__new__(A, *args, **kwargs) a = A() print(a.x)
class Singleton: def __new__(cls, *args, **kw): if not hasattr(cls, '_instance'): cls._instance = object.__new__(cls, *args, **kw) return cls._instance one = Singleton() two = Singleton() two.a = 3 print(one.a) # 3 # one和two彻底相同,能够用id(), ==, is检测 print(id(one)) # 29097904 print(id(two)) # 29097904 print(one == two) # True print(one is two) 单例模式
对象后面加括号,触发执行。
注:构造方法的执行是由建立对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print('__call__') obj = Foo() # 执行 __init__ obj() # 执行 __call__
class A: def __init__(self): self.a = 1 self.b = 2 def __len__(self): return len(self.__dict__) a = A() print(len(a))
class A: def __init__(self): self.a = 1 self.b = 2 def __hash__(self): return hash(str(self.a)+str(self.b)) a = A() print(hash(a))
class A: def __init__(self): self.a = 1 self.b = 2 def __eq__(self,obj): if self.a == obj.a and self.b == obj.b: return True a = A() b = A() print(a == b)
练习题
class FranchDeck: ranks = [str(n) for n in range(2,11)] + list('JQKA') suits = ['红心','方板','梅花','黑桃'] def __init__(self): self._cards = [Card(rank,suit) for rank in FranchDeck.ranks for suit in FranchDeck.suits] def __len__(self): return len(self._cards) def __getitem__(self, item): return self._cards[item] deck = FranchDeck() print(deck[0]) from random import choice print(choice(deck)) print(choice(deck))
class FranchDeck: ranks = [str(n) for n in range(2,11)] + list('JQKA') suits = ['红心','方板','梅花','黑桃'] def __init__(self): self._cards = [Card(rank,suit) for rank in FranchDeck.ranks for suit in FranchDeck.suits] def __len__(self): return len(self._cards) def __getitem__(self, item): return self._cards[item] def __setitem__(self, key, value): self._cards[key] = value deck = FranchDeck() print(deck[0]) from random import choice print(choice(deck)) print(choice(deck)) from random import shuffle shuffle(deck) print(deck[:5])
class Person: def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex def __hash__(self): return hash(self.name+self.sex) def __eq__(self, other): if self.name == other.name and self.sex == other.sex:return True p_lst = [] for i in range(84): p_lst.append(Person('egon',i,'male')) print(p_lst) print(set(p_lst))