视频课程《三-Python高级自动化开发工程师微职位:面向对象》
subprocess模块:
用途:如在Linux上调用windows的命令,需要获取命令的输出结果,而不是python代码的结果
与os.system的区别
输出命令结果到屏幕,返回值是Python代码运行的结果
subprocess把命令运行的结果和Python代码运行的结果都以元组形式返回,依次读取相应的值即可获得所需数据
Python2中类似的command
os.system("dir") # 返回状态 os.popen("dir").read() # 获取结果,但不明确"dir"的运行状态 # Linux上支持的比较好 import commands comands.getstatusoutput("dir") # 以元组形式返回状态值和命令运行的结果 # Python3.5后出现的方法 import subprocess subprocess.run(["df","-h"]) # 形式1 subprocess.run("df -h | grep sda1", shell=True) # 形式2 # Python3.5前的方法 subprocess.call(["ls", "-a"]) # 类似与os.system("ls") # 不需要获得命令运行结果,只需指导代码运行状态时 subprocess.check_call(["ls", "-l"]) # 返回0或者异常 subprocess.getstatusoutput("ls bin/ls") # 返回(0, "/bin/ls") subprocess.output("ls bin/ls") # 返回/bin/ls" subprocess.check_output(["ls", "-a"]) # 返回res,如:b'/bin/ls',并非打印
底层封装subprocess.popen()方法,返回到res对象中同时还能细分stderror
#res中只包含stdout的输出,从subprocess.PIPE读取而得 res = subprocess.Popen("ifconfig | grep 192", shell=True, stdout=subprocess.PIPE) res.stdout.read() # 获得stdout的数据 res = subprocess.Popen("ifconfig haha | grep 192, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) res.stdout.read() # 获取运行结果 res.stderr.read() # 获取错误信息
注意,每次调用相当于启动一个新的shell
res = subprocess.Popen("sleep 10; echo 'hi'", shell=True, stdout=subprocess.PIPE) res.poll() # 在命令执行完成后返回0,否则一直返回None,这是一个有趣的方法,注意理解 #或者可以使用阻塞的方法 res.wait() # 等待命令执行完,返回0 res.terminate()# 中止执行命令 res = subprocess.Popen("python3", shell=True, stdout=process.PIPE, stdin=subprocess.PIPE) res.stdin.write("print(1)") # 启动过程中写入数据 res.communicate(timeout=10) # 等待结果
例子:
# python中执行“sudo -S apt-get install vim”注意这个过程需要输入密码 subprocess.Popen("sudo -S apt-get install vim", shell=True, stdout=subprocess.PIPE) # 出错 # 明确命令“echo '1234' | sudo -S apt-get install vim”
面向对象
OOP利用类和对象对世界进行描述,使得程序维护和扩展变得简单,提高开发效率,代码容易理解。
Class类
抽象出属性的描述和方法。抽象的意义在于化繁为简,更有效率的描述信息。
Object对象
类的实例,对象真正存储着属性的值(通常意义上,类属性等除外)
Encapsulation封装
在类中对数据赋值、内部调用,外部调用不必也不能对类内进行修改访问。(对外部隐藏对象的工作细节)
Inheritance继承
代码复用,以普通类为基础建立专门的类
Polymorphism多态
对不同类的对象使用相同的操作,它们会有不同的结果。
“重复代码是可耻的行为”
“代码需要经常改动”
定义
原则:可读性好,易扩展
class Role(object): def __init__(self, name, role, value, money): self.name = name self.role = role self.life_value = value self.money = money def shot(self): print("shooting...") def got_shot(self): print("ah..., I got shot...") def buy_gun(self, gun): print("just bought %s", gun) # Python自动调用__del__方法,当对象内存被回收时,将会调用这个方法 def __del__(self): print("del ...run")
Python类定义的写法,其实就是执行代码块。
特性
普通属性(成员变量或字段)
在__init__()中确定,可以被直接访问和修改,也可以通过方法访问和修改。属于对象的,不同对象不能直接访问。
私有属性
形如self.__heart,obj.__heart无法外部访问,但实际上可以通过如下方法进行访问
def get_heart(self): # 通过方法访问 return self.__heart obj._Role__heart # 通过对象引用直接访问和修改
公有属性
类的所有实例都可以访问的属性,可以理解为类的属性。如果通过对象的引用修改了同名的公有属性,相当于对象添加了一个自己的普通属性。所以类的属性只能通过类名.nationality来修改。
方法
方法也是公有的,要注意理解。之所以要用对象的引用调用,因为定义时要通过self传递参数。
def shot2(self): print("run my own shot method, self.name r1.shot = shot2 r1.shot(r1) r2.shot() # 注意,r1对象shot方法对应的方法已经被外部定义的shot2方法替换,并且需要手动传递参数 # r2则使用的时原来的参数
析构方法
__del__(self),对象的引用被清理时会被自动调用,相当于“回调方法”在自定义对象被清理时,手动清理需要清理的变量。
关于继承机制
继承机制能够让子类或派生类在使用父类(基类)的功能,并且在无需编写原来功能代码的情况下对这些功能进行扩展。继承机制的实现有以下三种方式:
单继承
先继承再重写
class Person(object): person_count = 0 def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex def talk(self): print("Person is talking.") def inroll(self): # 类的计数器要使用类名调用 Person.person_count += 1 def tell(self): print("%s info" % self.name) for k,v in self.__dict__.items(): print(k, v) class BlackPerson(Person): def __init__(self, name, age, sex, strength): # 这里先继承了父类,然后重写了方法,并调用了父类的方法 # Person.__init__(self, name, age, sex) # 经典类写法 # 新式类写法,注意self参数的位置,以及理解调用机制 super(Person, self).__init__(name, age, sex) self.strength = strength print(self.name, self,age, self, sex) def talk(self): # 同名方法,子类会覆盖父类的方法 print("Black person is talking") def walk(self): # 子类对父类的拓展 print("Black is walking.")
多继承
再Python2和Python3里类的寻找路径是有区别的,Python2是深度优先(是否区分经典和新式?),Python3是广度优先。
# 此代码在windows下Python3环境运行 # 无论O是新式或经典写法,查询路径都是广度优先,很可能Python3内部实现已经全部归于新式类 class O(object): def __init__(self): print("init O") self.n = "O" class A(O): pass # def __init__(self): # print("init A") # self.n = "A" class B(O): # pass def __init__(self): print("init B") self.n = "B" class C(A, B): pass cc = C() print(cc.n)
但是利用在线解释器,使用Python2.7环境下会有区别,如图:
可以看出区别,Python2.7环境下,新式类广度优先,经典类深度优先(注意O的写法)
广度优先和深度优先的临界点
只有查找到A0时才会判断使用深度优先还是广度优先,如果再A0时查找O则是深度优先,如果去查找B1则是广度优先!
组合
关于多态
接口重用,通俗讲,就是通过父类调用子类的方法,体现出不同的形态。
class Animal(object): def talk(self): raise NotImplementedError("未实现该方法,报错") def real_talk(self): print(self.talk()) class Cat(Animal): def talk(self): return "Meow!" class Dog(Animal): def talk(self): return "Woof,Woof!" c = Cat() d = Dog() c.real_talk() d.real_talk()
补充
类,对象的关系
ssh应用案例:
# 整个过程还是抽象,抽象,抽象 class SSH: def __init__(self, host, port, pwd, username): self.host = host self.port = port self.qwd = pwd self.username = username def connection(self): # 创建链接 # self.conn = 链接对象() pass def close(self): # 关闭链接 pass def upload(self): pass def com(self): pass obj = SSH()
函数保存在类里,字段保存在对象里。
静态属性(共有属性)
封装的理解:
类封装了字段名和方法,“抽象”是封装的方法论;
对象封装了字段的值,也就是类的实体;
类方法寻找的顺序,要注意F2的a1方法里调用self.a2(),self.a2()寻找的起点仍然是F3
class F1: def __init__(self): print("F1") def a1(self): print("F1a1") def a2(self): print("F1a2") class F2(F1): def __init__(self): print("F2") def a1(self): self.a2() print("F2a1") def a2(self): print("F2a2") class F3(F2): def __init__(self): print("F3") def a2(self): print("F3a2") obj = F3() obj.a1() # F3a2 F2a1
静态方法 @staticmethod
保存在类里
class F1: def __init__(self): print("F1") @staticmethod def a1(): # 可以没有self参数,也可以设定任意参数 print("F1a1") F1.a1() # 通过类来访问
Python是否有“静态变量”?
Python是动态语言,它的类严格讲没有静态变量,不过从特性上来讲,类变量可以称为“静态变量”
子类调用父类构造方法
# 两种方法可以实现 class A: def __init__(self): self.hungry = True class B: def __init__(self): A.__init__(self) # 方法一,调用父类的未绑定构造方法 # super(A, self).__init__() # 方法二,super函数返回的引用相当于父类对象的引用,可以直接调用。 self.sound = "Squawk!"
两种方法均可以调用,通过super函数调用的写法是新式类的写法,而利用父类的未绑定构造方法并传入self引用的调用方法是“传统阵营”的写法。所以使用super更被推荐。