视频课程《三-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的写法)

广度优先和深度优先的临界点

Python面向对象学习笔记_第1张图片

只有查找到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更被推荐。