shelve 模块
shelve模块是一个简单的k,v将内存数据通过文件持久化的模块,可以持久化任何pickle可支持的python数据格式
1 import shelve 2 3 d = shelve.open('shelve_test') #打开一个文件 4 5 class Test(object): 6 def __init__(self,n): 7 self.n = n 8 9 10 t = Test(123) 11 t2 = Test(123334) 12 13 name = ["alex","rain","test"] 14 d["test"] = name #持久化列表 15 d["t1"] = t #持久化类 16 d["t2"] = t2 17 18 d.close()
xml处理模块
xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单,不过,古时候,在json还没诞生的黑暗年代,大家只能选择用xml呀,至今很多传统公司如金融行业的很多系统的接口还主要是xml。
xml的格式如下,就是通过<>节点来区别数据结构的:
1 "1.0"?> 2 3"Liechtenstein"> 4 10"yes">2 52008 6141100 7"Austria" direction="E"/> 8 "Switzerland" direction="W"/> 9 "Singapore"> 11 16"yes">5 122011 1359900 14"Malaysia" direction="N"/> 15 "Panama"> 17 23"yes">69 182011 1913600 20"Costa Rica" direction="W"/> 21 "Colombia" direction="E"/> 22
xml协议在各个语言里的都 是支持的,在python中可以用以下模块操作xml
import xml.etree.ElementTree as ET tree = ET.parse("test.xml") root = tree.getroot() print(root.tag) #遍历xml文档 for child in root: print(child.tag, child.attrib) #打印文件节点和文件属性 for i in child: print(i.tag,i.text) #只遍历year 节点 for node in root.iter('year'): #iter相当于过滤的意思,把所有的year过滤出来 print(node.tag,node.text)
修改和删除xml文档内容
1 import xml.etree.ElementTree as ET 2 3 tree = ET.parse("test.xml") 4 root = tree.getroot() 5 6 #修改 7 for node in root.iter('year'): 8 new_year = int(node.text) + 1 9 node.text = str(new_year) 10 node.set("updated","yes") 11 12 tree.write("test.xml") 13 14 15 #删除node 16 for country in root.findall('country'): 17 rank = int(country.find('rank').text) 18 if rank > 50: 19 root.remove(country) 20 21 tree.write('output.xml')
自己创建xml文档
1 import xml.etree.ElementTree as ET 2 3 4 new_xml = ET.Element("namelist") 5 name = ET.SubElement(new_xml,"name",attrib={"enrolled":"yes"}) 6 age = ET.SubElement(name,"age",attrib={"checked":"no"}) 7 sex = ET.SubElement(name,"sex") 8 sex.text = '33' 9 name2 = ET.SubElement(new_xml,"name",attrib={"enrolled":"no"}) 10 age = ET.SubElement(name2,"age") 11 age.text = '19' 12 13 et = ET.ElementTree(new_xml) #生成文档对象 14 et.write("test.xml", encoding="utf-8",xml_declaration=True) 15 16 ET.dump(new_xml) #打印生成的格式
PyYAML模块
Python也可以很容易的处理ymal文档格式,只不过需要安装一个模块,参考文档:http://pyyaml.org/wiki/PyYAMLDocumentation
ConfigParser模块
用于生成和修改常见配置文档,当前模块的名称在 python 3.x 版本中变更为 configparser。
来看一个好多软件的常见文档格式如下
1 [DEFAULT] 2 ServerAliveInterval = 45 3 Compression = yes 4 CompressionLevel = 9 5 ForwardX11 = yes 6 7 [bitbucket.org] 8 User = hg 9 10 [topsecret.server.com] 11 Port = 50022 12 ForwardX11 = no
如果想用python生成一个这样的文档怎么做呢?
1 import configparser 2 3 config = configparser.ConfigParser() 4 config["DEFAULT"] = {'ServerAliveInterval': '45', 5 'Compression': 'yes', 6 'CompressionLevel': '9'} 7 8 config['bitbucket.org'] = {} 9 config['bitbucket.org']['User'] = 'hg' 10 config['topsecret.server.com'] = {} 11 topsecret = config['topsecret.server.com'] 12 topsecret['Host Port'] = '50022' # mutates the parser 13 topsecret['ForwardX11'] = 'no' # same here 14 config['DEFAULT']['ForwardX11'] = 'yes' 15 with open('example.ini', 'w') as configfile: 16 config.write(configfile)
写完了还可以再读出来哈。
1 >>> import configparser 2 >>> config = configparser.ConfigParser() 3 >>> config.sections() 4 [] 5 >>> config.read('example.ini') 6 ['example.ini'] 7 >>> config.sections() 8 ['bitbucket.org', 'topsecret.server.com'] 9 >>> 'bitbucket.org' in config 10 True 11 >>> 'bytebong.com' in config 12 False 13 >>> config['bitbucket.org']['User'] 14 'hg' 15 >>> config['DEFAULT']['Compression'] 16 'yes' 17 >>> topsecret = config['topsecret.server.com'] 18 >>> topsecret['ForwardX11'] 19 'no' 20 >>> topsecret['Port'] 21 '50022' 22 >>> for key in config['bitbucket.org']: print(key) 23 ... 24 user 25 compressionlevel 26 serveraliveinterval 27 compression 28 forwardx11 29 >>> config['bitbucket.org']['ForwardX11'] 30 'yes'
configparser增删改查语法
[section1] k1 = v1 k2:v2 [section2] k1 = v1 import ConfigParser config = ConfigParser.ConfigParser() config.read('i.cfg') # ########## 读 ########## #secs = config.sections() #print secs #options = config.options('group2') #print options #item_list = config.items('group2') #print item_list #val = config.get('group1','key') #val = config.getint('group1','key') # ########## 改写 ########## #sec = config.remove_section('group1') #config.write(open('i.cfg', "w")) #sec = config.has_section('wupeiqi') #config.add_section('luolingfeng') #config.write(open('i.cfg', "w")) #config.set('group2','k1','11111') #config.write(open('i.cfg', "w")) #config.remove_option('group2','age') #config.write(open('i.cfg', "w"))
hashlib模块
用于加密相关的操作,3.x里代替了md5模块和sha模块,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法
1 import hashlib 2 3 m = hashlib.md5() 4 m.update(b"Hello") 5 m.update(b"It's me") 6 print(m.digest()) 7 m.update(b"It's been a long time since last time we ...") 8 9 print(m.digest()) #2进制格式hash 10 print(len(m.hexdigest())) #16进制格式hash 11 ''' 12 def digest(self, *args, **kwargs): # real signature unknown 13 """ Return the digest value as a string of binary data. """ 14 pass 15 16 def hexdigest(self, *args, **kwargs): # real signature unknown 17 """ Return the digest value as a string of hexadecimal digits. """ 18 pass 19 20 ''' 21 import hashlib 22 23 # ######## md5 ######## 24 25 hash = hashlib.md5() 26 hash.update('admin') 27 print(hash.hexdigest()) 28 29 # ######## sha1 ######## 30 31 hash = hashlib.sha1() 32 hash.update('admin') 33 print(hash.hexdigest()) 34 35 # ######## sha256 ######## 36 37 hash = hashlib.sha256() 38 hash.update('admin') 39 print(hash.hexdigest()) 40 41 42 # ######## sha384 ######## 43 44 hash = hashlib.sha384() 45 hash.update('admin') 46 print(hash.hexdigest()) 47 48 # ######## sha512 ######## 49 50 hash = hashlib.sha512() 51 hash.update('admin') 52 print(hash.hexdigest())
还不够吊?python 还有一个 hmac 模块,它内部对我们创建 key 和 内容 再进行处理然后再加密
1 import hmac 2 h = hmac.new('wueiqi') 3 h.update('hellowo') 4 print h.hexdigest()
更多关于md5,sha1,sha256等介绍的文章看这里
https://www.tbs-certificates.co.uk/FAQ/en/sha256.html
Subprocess模块
The subprocess
module allows you to spawn new processes, connect to their input/output/error pipes, and obtain their return codes. This module intends to replace several older modules and functions:
os.system
os.spawn*
The recommended approach to invoking subprocesses is to use the run()
function for all use cases it can handle. For more advanced use cases, the underlying Popen
interface can be used directly.
The run()
function was added in Python 3.5; if you need to retain compatibility with older versions, see the Older high-level API section.
subprocess.
run
(args, *, stdin=None, input=None, stdout=None, stderr=None, shell=False, timeout=None, check=False)
Run the command described by args. Wait for command to complete, then return a CompletedProcess
instance.
The arguments shown above are merely the most common ones, described below in Frequently Used Arguments (hence the use of keyword-only notation in the abbreviated signature). The full function signature is largely the same as that of the Popen
constructor - apart from timeout, input and check, all the arguments to this function are passed through to that interface.
This does not capture stdout or stderr by default. To do so, pass PIPE
for the stdout and/or stderr arguments.
The timeout argument is passed to Popen.communicate()
. If the timeout expires, the child process will be killed and waited for. The TimeoutExpired
exception will be re-raised after the child process has terminated.
The input argument is passed to Popen.communicate()
and thus to the subprocess’s stdin. If used it must be a byte sequence, or a string if universal_newlines=True
. When used, the internal Popen
object is automatically created withstdin=PIPE
, and the stdin argument may not be used as well.
If check is True, and the process exits with a non-zero exit code, a CalledProcessError
exception will be raised. Attributes of that exception hold the arguments, the exit code, and stdout and stderr if they were captured.
1 >>> subprocess.run(["ls", "-l"]) # doesn't capture output 2 CompletedProcess(args=['ls', '-l'], returncode=0) 3 4 >>> subprocess.run("exit 1", shell=True, check=True) 5 Traceback (most recent call last): 6 ... 7 subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1 8 9 >>> subprocess.run(["ls", "-l", "/dev/null"], stdout=subprocess.PIPE) 10 CompletedProcess(args=['ls', '-l', '/dev/null'], returncode=0, 11 stdout=b'crw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null\n')
调用subprocess.run(...)是推荐的常用方法,在大多数情况下能满足需求,但如果你可能需要进行一些复杂的与系统的交互的话,你还可以用subprocess.Popen(),语法如下:
1 p = subprocess.Popen("find / -size +1000000 -exec ls -shl {} \;",shell=True,stdout=subprocess.PIPE) 2 print(p.stdout.read())
可用参数:
-
- args:shell命令,可以是字符串或者序列类型(如:list,元组)
- bufsize:指定缓冲。0 无缓冲,1 行缓冲,其他 缓冲区大小,负值 系统缓冲
- stdin, stdout, stderr:分别表示程序的标准输入、输出、错误句柄
- preexec_fn:只在Unix平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用
- close_sfs:在windows平台下,如果close_fds被设置为True,则新创建的子进程将不会继承父进程的输入、输出、错误管道。
所以不能将close_fds设置为True同时重定向子进程的标准输入、输出与错误(stdin, stdout, stderr)。 - shell:同上
- cwd:用于设置子进程的当前目录
- env:用于指定子进程的环境变量。如果env = None,子进程的环境变量将从父进程中继承。
- universal_newlines:不同系统的换行符不同,True -> 同意使用 \n
- startupinfo与createionflags只在windows下有效
将被传递给底层的CreateProcess()函数,用于设置子进程的一些属性,如:主窗口的外观,进程的优先级等等
终端输入的命令分为两种:
- 输入即可得到输出,如:ifconfig
- 输入进行某环境,依赖再输入,如:python
需要交互的命令示例
1 import subprocess 2 3 obj = subprocess.Popen(["python"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 4 obj.stdin.write('print 1 \n ') 5 obj.stdin.write('print 2 \n ') 6 obj.stdin.write('print 3 \n ') 7 obj.stdin.write('print 4 \n ') 8 9 out_error_list = obj.communicate(timeout=10) 10 print out_error_list
logging模块
很多程序都有记录日志的需求,并且日志中包含的信息即有正常的程序访问日志,还可能有错误、警告等信息输出,python的logging模块提供了标准的日志接口,你可以通过它存储各种格式的日志,logging的日志可以分为 debug()
, info()
, warning()
, error()
and critical() 5个级别,
下面我们看一下怎么用。
最简单用法
1 import logging 2 3 logging.warning("user [alex] attempted wrong password more than 3 times") 4 logging.critical("server is down") 5 6 #输出 7 WARNING:root:user [alex] attempted wrong password more than 3 times 8 CRITICAL:root:server is down
对于等级:
1 CRITICAL = 50 2 FATAL = CRITICAL 3 ERROR = 40 4 WARNING = 30 5 WARN = WARNING 6 INFO = 20 7 DEBUG = 10 8 NOTSET = 0
看一下这几个日志级别分别代表什么意思
Level | When it’s used |
---|---|
DEBUG |
Detailed information, typically of interest only when diagnosing problems. |
INFO |
Confirmation that things are working as expected. |
WARNING |
An indication that something unexpected happened, or indicative of some problem in the near future (e.g. ‘disk space low’). The software is still working as expected. |
ERROR |
Due to a more serious problem, the software has not been able to perform some function. |
CRITICAL |
A serious error, indicating that the program itself may be unable to continue running. |
如果想把日志写到文件里,也很简单
1 import logging 2 3 logging.basicConfig(filename='example.log',level=logging.INFO) 4 logging.debug('This message should go to the log file') 5 logging.info('So should this') 6 logging.warning('And this, too')
其中下面这句中的level=loggin.INFO意思是,把日志纪录级别设置为INFO,也就是说,只有比日志是INFO或比INFO级别更高的日志才会被纪录到文件里,在这个例子, 第一条日志是不会被纪录的,如果希望纪录debug的日志,那把日志级别改成DEBUG就行了。
1 logging.basicConfig(filename='example.log',level=logging.INFO)
感觉上面的日志格式忘记加上时间啦,日志不知道时间怎么行呢,下面就来加上!
1 import logging 2 logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p') 3 logging.warning('is when this event was logged.') 4 5 #输出 6 12/12/2010 11:46:36 AM is when this event was logged.
如果想同时把log打印在屏幕和文件日志里,就需要了解一点复杂的知识 了
The logging library takes a modular approach and offers several categories of components: loggers, handlers, filters, and formatters.
- Loggers expose the interface that application code directly uses.
- Handlers send the log records (created by loggers) to the appropriate destination.
- Filters provide a finer grained facility for determining which log records to output.
- Formatters specify the layout of log records in the final output.
1 import logging 2 3 #create logger 4 logger = logging.getLogger('TEST-LOG') 5 logger.setLevel(logging.DEBUG) 6 7 8 # create console handler and set level to debug 9 ch = logging.StreamHandler() 10 ch.setLevel(logging.DEBUG) 11 12 # create file handler and set level to warning 13 fh = logging.FileHandler("access.log") 14 fh.setLevel(logging.WARNING) 15 # create formatter 16 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') 17 18 # add formatter to ch and fh 19 ch.setFormatter(formatter) 20 fh.setFormatter(formatter) 21 22 # add ch and fh to logger 23 logger.addHandler(ch) 24 logger.addHandler(fh) 25 26 # 'application' code 27 logger.debug('debug message') 28 logger.info('info message') 29 logger.warn('warn message') 30 logger.error('error message') 31 logger.critical('critical message')
对于格式,有如下属性可是配置:
面向对象学习
- 写重复代码是非常不好的低级行为
- 你写的代码需要经常变更
开发正规的程序跟那种写个运行一次就扔了的小脚本一个很大不同就是,你的代码总是需要不断的更改,不是修改bug就是添加新功能等,所以为了日后方便程序的修改及扩展,你写的代码一定要遵循易读、易改的原则(专业数据叫可读性好、易扩展)。
1 #role 1 2 name = 'Alex' 3 role = 'terrorist' 4 weapon = 'AK47' 5 life_value = 100 6 7 #rolw 2 8 name2 = 'Jack' 9 role2 = 'police' 10 weapon2 = 'B22' 11 life_value2 = 100
上面定义了一个恐怖份子Alex和一个警察Jack,但只2个人不好玩呀,一干就死了,没意思,那我们再分别一个恐怖分子和警察吧,
1 #role 1 2 name = 'Alex' 3 role = 'terrorist' 4 weapon = 'AK47' 5 life_value = 100 6 money = 10000 7 8 #rolw 2 9 name2 = 'Jack' 10 role2 = 'police' 11 weapon2 = 'B22' 12 life_value2 = 100 13 money2 = 10000 14 15 #role 3 16 name3 = 'Rain' 17 role3 = 'terrorist' 18 weapon3 = 'C33' 19 life_value3 = 100 20 money3 = 10000 21 22 #rolw 4 23 name4 = 'Eric' 24 role4 = 'police' 25 weapon4 = 'B51' 26 life_value4 = 100 27 money4 = 10000
4个角色虽然创建好了,但是有个问题就是,每创建一个角色,我都要单独命名,name1,name2,name3,name4…,后面的调用的时候这个变量名你还都得记着,要是再让多加几个角色,估计调用时就很容易弄混啦,所以我们想一想,能否所有的角色的变量名都是一样的,但调用的时候又能区分开分别是谁?
当然可以,我们只需要把上面的变量改成字典的格式就可以啦。
1 roles = { 2 1:{'name':'Alex', 3 'role':'terrorist', 4 'weapon':'AK47', 5 'life_value': 100, 6 'money': 15000, 7 }, 8 2:{'name':'Jack', 9 'role':'police', 10 'weapon':'B22', 11 'life_value': 100, 12 'money': 15000, 13 }, 14 3:{'name':'Rain', 15 'role':'terrorist', 16 'weapon':'C33', 17 'life_value': 100, 18 'money': 15000, 19 }, 20 4:{'name':'Eirc', 21 'role':'police', 22 'weapon':'B51', 23 'life_value': 100, 24 'money': 15000, 25 }, 26 } 27 28 print(roles[1]) #Alex 29 print(roles[2]) #Jack
- 被打中后就会掉血的功能
- 开枪功能
- 换子弹
- 买枪
- 跑、走、跳、下蹲等动作
- 保护人质(仅适用于警察)
- 不能杀同伴
- 。。。
1 def shot(by_who): 2 #开了枪后要减子弹数 3 pass 4 def got_shot(who): 5 #中枪后要减血 6 who[‘life_value’] -= 10 7 pass 8 def buy_gun(who,gun_name): 9 #检查钱够不够,买了枪后要扣钱 10 pass 11 ...
- 每个角色定义的属性名称是一样的,但这种命名规则是我们自己约定的,从程序上来讲,并没有进行属性合法性检测,也就是说role 1定义的代表武器的属性是weapon, role 2 ,3,4也是一样的,不过如果我在新增一个角色时不小心把weapon 写成了wepon , 这个程序本身是检测 不到的
- terrorist 和police这2个角色有些功能是不同的,比如police是不能杀人质的,但是terrorist可能,随着这个游戏开发的更复杂,我们会发现这2个角色后续有更多的不同之处, 但现在的这种写法,我们是没办法 把这2个角色适用的功能区分开来的,也就是说,每个角色都可以直接调用任意功能,没有任何限制。
- 我们在上面定义了got_shot()后要减血,也就是说减血这个动作是应该通过被击中这个事件来引起的,我们调用get_shot(),got_shot()这个函数再调用每个角色里的life_value变量来减血。 但其实我不通过got_shot(),直接调用角色roles[role_id][‘life_value’] 减血也可以呀,但是如果这样调用的话,那可以就是简单粗暴啦,因为减血之前其它还应该判断此角色是否穿了防弹衣等,如果穿了的话,伤害值肯定要减少,got_shot()函数里就做了这样的检测,你这里直接绕过的话,程序就乱了。 因此这里应该设计 成除了通过got_shot(),其它的方式是没有办法给角色减血的,不过在上面的程序设计里,是没有办法实现的。
- 现在需要给所有角色添加一个可以穿防弹衣的功能,那很显然你得在每个角色里放一个属性来存储此角色是否穿 了防弹衣,那就要更改每个角色的代码,给添加一个新属性,这样太low了,不符合代码可复用的原则
上面这4点问题如果不解决,以后肯定会引出更大的坑,有同学说了,解决也不复杂呀,直接在每个功能调用时做一下角色判断啥就好了,没错,你要非得这么霸王硬上弓的搞也肯定是可以实现的,那你自己就开发相应的代码来对上面提到的问题进行处理好啦。 但这些问题其实能过OOP就可以很简单的解决。
之前的代码改成用OOP中的“类”来实现的话如下:
1 class Role(object): 2 def __init__(self,name,role,weapon,life_value=100,money=15000): 3 self.name = name 4 self.role = role 5 self.weapon = weapon 6 self.life_value = life_value 7 self.money = money 8 9 def shot(self): 10 print("shooting...") 11 12 def got_shot(self): 13 print("ah...,I got shot...") 14 15 def buy_gun(self,gun_name): 16 print("just bought %s" %gun_name) 17 18 r1 = Role('Alex','police','AK47’) #生成一个角色 19 r2 = Role('Jack','terrorist','B22’) #生成一个角色
- 代码量少了近一半
- 角色和它所具有的功能可以一目了然看出来
接下来我们一起分解一下上面的代码分别 是什么意思
1 class Role(object): #定义一个类, class是定义类的语法,Role是类名,(object)是新式类的写法,必须这样写,以后再讲为什么 2 def __init__(self,name,role,weapon,life_value=100,money=15000): #初始化函数,在生成一个角色时要初始化的一些属性就填写在这里 3 self.name = name #__init__中的第一个参数self,和这里的self都 是什么意思? 看下面解释 4 self.role = role 5 self.weapon = weapon 6 self.life_value = life_value 7 self.money = money
上面的这个__init__()叫做初始化方法(或构造方法), 在类被调用时,这个方法(虽然它是函数形式,但在类中就不叫函数了,叫方法)会自动执行,进行一些初始化的动作,所以我们这里写的__init__(self,name,role,weapon,life_value=100,money=15000)就是要在创建一个角色时给它设置这些属性,那么这第一个参数self是干毛用的呢?
初始化一个角色,就需要调用这个类一次:
r1 = Role('Alex','police','AK47’) #生成一个角色 , 会自动把参数传给Role下面的__init__(...)方法 r2 = Role('Jack','terrorist','B22’) #生成一个角色
我们看到,上面的创建角色时,我们并没有给__init__传值,程序也没未报错,是因为,类在调用它自己的__init__(…)时自己帮你给self参数赋值了
1 r1 = Role('Alex','police','AK47’) #此时self 相当于 r1 , Role(r1,'Alex','police','AK47’) 2 r2 = Role('Jack','terrorist','B22’)#此时self 相当于 r2, Role(r2,'Jack','terrorist','B22’)
- 在内存中开辟一块空间指向r1这个变量名
- 调用Role这个类并执行其中的__init__(…)方法,相当于Role.__init__(r1,'Alex','police',’AK47’),这么做是为什么呢? 是为了把'Alex','police',’AK47’这3个值跟刚开辟的r1关联起来,是为了把'Alex','police',’AK47’这3个值跟刚开辟的r1关联起来,是为了把'Alex','police',’AK47’这3个值跟刚开辟的r1关联起来,重要的事情说3次, 因为关联起来后,你就可以直接r1.name, r1.weapon 这样来调用啦。所以,为实现这种关联,在调用__init__方法时,就必须把r1这个变量也传进去,否则__init__不知道要把那3个参数跟谁关联呀。
- 明白了么哥?所以这个__init__(…)方法里的,self.name = name , self.role = role 等等的意思就是要把这几个值 存到r1的内存空间里。
1 def buy_gun(self,gun_name): 2 print(“%s has just bought %s” %(self.name,gun_name) )
上面这个方法通过类调用的话要写成如下:
1 r1 = Role('Alex','police','AK47') 2 r1.buy_gun("B21”) #python 会自动帮你转成 Role.buy_gun(r1,”B21")
- 上面的这个r1 = Role('Alex','police','AK47’)动作,叫做类的“实例化”, 就是把一个虚拟的抽象的类,通过这个动作,变成了一个具体的对象了, 这个对象就叫做实例
- 刚才定义的这个类体现了面向对象的第一个基本特性,封装,其实就是使用构造方法将内容封装到某个具体对象中,然后通过对象直接或者self间接获取被封装的内容
面向对象的特性:
封装
封装最好理解了。封装是面向对象的特征之一,是对象和类概念的主要特性。
封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
继承
面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
通过继承创建的新类称为“子类”或“派生类”。
被继承的类称为“基类”、“父类”或“超类”。
继承的过程,就是从一般到特殊的过程。
要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。
在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。
继承概念的实现方式有三类:实现继承、接口继承和可视继承。
在考虑使用继承时,有一点需要注意,那就是两个类之间的关系应该是“属于”关系。例如,Employee 是一个人,Manager 也是一个人,因此这两个类都可以继承 Person 类。但是 Leg 类却不能继承 Person 类,因为腿并不是一个人。
OO开发范式大致为:划分对象→抽象类→将类组织成为层次化结构(继承和合成) →用类与实例进行设计和实现几个阶段。
1 #!_*_coding:utf-8_*_ 2 #__author__:"Alex Li" 3 4 5 6 class SchoolMember(object): 7 members = 0 #初始学校人数为0 8 def __init__(self,name,age): 9 self.name = name 10 self.age = age 11 12 def tell(self): 13 pass 14 15 def enroll(self): 16 '''注册''' 17 SchoolMember.members +=1 18 print("\033[32;1mnew member [%s] is enrolled,now there are [%s] members.\033[0m " %(self.name,SchoolMember.members)) 19 20 def __del__(self): 21 '''析构方法''' 22 print("\033[31;1mmember [%s] is dead!\033[0m" %self.name) 23 class Teacher(SchoolMember): 24 def __init__(self,name,age,course,salary): 25 super(Teacher,self).__init__(name,age) 26 self.course = course 27 self.salary = salary 28 self.enroll() 29 30 31 def teaching(self): 32 '''讲课方法''' 33 print("Teacher [%s] is teaching [%s] for class [%s]" %(self.name,self.course,'s12')) 34 35 def tell(self): 36 '''自我介绍方法''' 37 msg = '''Hi, my name is [%s], works for [%s] as a [%s] teacher !''' %(self.name,'Oldboy', self.course) 38 print(msg) 39 40 class Student(SchoolMember): 41 def __init__(self, name,age,grade,sid): 42 super(Student,self).__init__(name,age) 43 self.grade = grade 44 self.sid = sid 45 self.enroll() 46 47 48 def tell(self): 49 '''自我介绍方法''' 50 msg = '''Hi, my name is [%s], I'm studying [%s] in [%s]!''' %(self.name, self.grade,'Oldboy') 51 print(msg) 52 53 if __name__ == '__main__': 54 t1 = Teacher("Alex",22,'Python',20000) 55 t2 = Teacher("TengLan",29,'Linux',3000) 56 57 s1 = Student("Qinghua", 24,"Python S12",1483) 58 s2 = Student("SanJiang", 26,"Python S12",1484) 59 60 t1.teaching() 61 t2.teaching() 62 t1.tell()