48小时系统班试听入口

【运维技术点】python面向对象

发布作者:新盟教育 发布日期:2022-11-01 浏览人数:5313人

运维技术点


面向对象的编程(object oriented programming),简称OOP:是一种编程的思想。OOP把对象当成一个程序的基本单元,一个对象包含了数据和操作数据的函数。面向对象的出现极大的提高了编程的效率,使其编程的重用性增高。

模拟场景理解面向对象和面向过程:


1 ''' 2 使用面向过程的思想解决吃饭的问题? 3 步骤: 4 1).思考今天吃什么? 5 2).去买菜(货比三家) 6 3).摘菜 7 4).洗菜 8 5).切菜 9 6).炒菜10 7).焖饭11 8).吃饭12 9).洗刷13 使用面向对象的思想解决吃饭的问题?14 步骤:15 1).思考今天吃什么?16 2).去饭店17 ①.调用服务员的点菜功能18 ②.将菜品告知后台大厨19 ③.大厨调用服务员的上菜功能20 3).开始吃饭21 4).结账走人(多种支付方式)22 '''


Python是解释性语言,但是它是面向对象的,能够进行对象编程。面向对象是一种编程方式,此编程方式的实现是基于对类和对象的使用;类是一个模板,模板中包装了多个“函数”供使用(可以讲多函数中公用的变量封装到对象中);

对象,根据模板创建的实例(即:对象),实例用于调用被包装在类中的函数。大白话理解类和对象的区别:i类:具有一些列相同特征、行为的"事物",它的表现是不具体的、不清晰的、模糊的概念;对象:从类中实例化得到,一个实实在在的个体,在内存中有体现;它的表现是具体的、清晰的、看得见摸的着的。

遇到面向对象的问题,通常可以考虑如下三个环节:

1)设计类,定义属性、函数、...(可能需要花费大量的时间) ;2)创建(实例化)对象(简单,一行代码搞定,但是内存比较复杂);3)对象调用属性或者函数完成需求。

 1 # 1.设计类 2 class Car(object):  3       4     # 属性 5     color = "红色" 6     brand = "BMW" 7     number = "沪A88888" 8  9     # 函数/方法10     def run(self):11         print('%s的%s,车牌为%s,正在飞速的行驶...' %(self.color,self.brand,self.number))12 13     def stop(self):14         print('车停了...')15 16 # 2.创建对象/实例化对象17 c1 = Car()18 print(c1,type(c1))                  # 得到<__main__.Car object at 0x000000000220D710> <class '__main__.Car'>19 20 # 3.对象调用属性21 print(c1.color,c1.brand,c1.number)  # 红色 BMW 沪A8888822 23 # 4.对象调用函数24 c1.run()                            # 红色的BMW,车牌为沪A88888,正在飞速的行驶...25 c1.stop()                           # 车停了...26 27 # 创建第二个对象28 c2 = Car()29 print(c2,type(c2))                  # 得到<__main__.Car object at 0x000000000272D7B8> <class '__main__.Car'>30 31 print(c1 == c2)                     # 得到False ,比较的是地址,c1和c2的地址不一样32 33 c2.color = "白色"34 c2.brand = "BYD"35 c2.number = "京A66666"36 print(c2.color,c2.brand,c2.number)   # 白色 BYD 京A6666637 print(c1.color,c1.brand,c1.number)   # 红色 BMW 沪A8888838 '''在一个模块中可以创建多个对象,它们彼此之间是相互独立存在(堆空间有体现),切互不干扰...'''39 40 c3 = c1                              # c1和c3的地址是一样的,共用,c1不调用了,但c3还指在堆上,c1记录的地址又给到c3一份41 c1 = None42 '''此时堆中有1个空间,不存在垃圾空间;因为c1虽然被赋值为None了,但是c3仍然记录了堆中对象空间的地址(维护这层关系)'''


类的成员:

1)字段:普通字段、静态字段

普通字段需要通过对象来访问,而静态字段通过类访问,在使用上可以看出普通字段和静态字段的归属是不同的;他们的本质的区别是内存中保存的位置不同,普通字段属于对象,而静态字段属于类;静态字段在内存中只保存一份,普通字段在每个对象中都要保存一份;通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段。


1 class Province: 2 3 # 静态字段/类属性 4 country = '中国' 5 6 def __init__(self, name): 7 # 普通字段/对象属性 8 self.name = name 9 10 # 实例对象obj111 obj1 = Province('河北省')12 print(obj.name) # 直接访问普通字段13 14 # 实例对象obj215 obj2 = Province('河南省')16 print(obj.name) # 直接访问普通字段17 18 # 直接访问静态字段19 Province.country


2)方法:普通方法、类方法、静态方法、属性方法

前三种方法在内存中都归属于类,区别在于调用方式不同。普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self;类方法:由类调用;至少一个cls参数;执行类方法时,自动将调用该方法的类复制给cls;静态方法:由类调用;无默认参数。

相同点:对于所有的方法而言,均属于类(非对象)中,所以,在内存中也只保存一份;不同点:方法调用者不同、调用方法时自动传入的参数不同。

静态方法意思是把 @staticmethod 下面的函数和所属的类截断了,这个函数就不属于这个类了,没有类的属性了,只不是还是要通过类名的方式调用 ,把静态方法当作一个独立的函数给他传参就行了。

类方法只能访问类变量,不能访问实例变量。

属性方法把一个方法变成一个静态属性,属性就不用加小括号那样的去调用了。


1 class Foo: 2 3 def __init__(self, name): 4 self.name = name 5 6 def ord_func(self): 7 """ 定义普通方法,至少有一个self参数 """ 8 print('普通方法') 9 10 @classmethod11 def class_func(cls):12 """ 定义类方法,至少有一个cls参数 """13 print('类方法')14 15 @staticmethod16 def static_func():17 """ 定义静态方法 ,无默认参数"""18 print('静态方法')19 20 # 调用普通方法21 f = Foo()22 f.ord_func()23 24 # 调用类方法25 Foo.class_func()26 27 # 调用静态方法28 Foo.static_func()


属性方法:其实是方法里面普通方法的变种。属性方法的定义和调用要注意:定义时,在普通方法的基础上添加@property装饰器;定义时,属性仅有一个self参数调用时,无需括号。补充:属性存在意义是:访问属性时可以制造出和访问字段完全相同的假象,属性由方法变种而来,如果Python中没有属性,方法完全可以代替其功能。Python的属性的功能是:属性内部进行一系列的逻辑计算,最终将计算结果返回。


1 class Foo: 2 3 # 定义普通方法 4 def func(self): 5 pass 6 7 # 定义属性 8 @property 9 def prop(self):10 pass11 12 # 实例化对象13 foo_obj = Foo()14 15 foo_obj.func() # 调用普通方法16 foo_obj.prop # 调用属性方法


属性方法的两种定义方式:装饰器,即:在方法上应用装饰器;静态字段,即:在类中定义值为property对象的静态字段。


1 # 装饰器方式:在类的普通方法上应用@property装饰器 2 class Goods: 3 4 @property 5 def price(self): 6 return "wupeiqi" 7 8 obj = Goods() 9 10 obj.price # 自动执行 @property 修饰的 price 方法,并获取方法的返回值11 12 13 # 静态字段方式,创建值为property对象的静态字段14 class Foo:15 16 def get_bar(self):17 return 'wupeiqi'18 19 BAR = property(get_bar)20 21 obj = Foo()22 23 obj.BAR # 自动调用get_bar方法,并获取方法的返回值


类特殊成员:

魔术函数:

__开头并且__结尾的函数,我们称为魔术函数;特点:调用执行都不需要程序员关注,系统自行决定;例如:__init__、__del__、__str__ 、...... 构造函数,析构函数, 重写函数,...... 。

构造函数(constructor):

又称构造方法/构造器,在生成对象时调用,一个对象只会被执行一次,可以用来进行一些初始化操作,不需要显示去调用,系统会默认去执行。

格式:__init__(self):

执行时机:在创建对象时被执行。


1 class Person(object): 2 3 def __init__(self,name,age): 4 self.name = name 5 self.age = age 6 self.address = '中国' 6 7 def details(self): 8 print('姓名为:%s,年龄为:%d,籍贯是:%s' %(self.name,self.age,self.address)) 9 10 # 创建对象11 p1 = Person("多多",18) # 构造方法,通过类创建对象时,自动触发执行
12 # 创建第二个对象,与p1互不干扰,共同存在与堆中13 p2 = Person("老王",28)14 p2.details()


析构函数:

当对象在内存中被释放时,自动触发执行,此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行。

所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。这个方法默认是不需要写的,不写的时候,默认是不做任何操作的。因为不知道对象是在什么时候被垃圾回收掉,所以,除非确实要在这里面做某些操作,不然不要自定义这个方法。

格式:__del__(self):

执行时机:在程序结束前,将对象回收,清出内存。


1 class Dog: 2 3 def __init__(self,name,age,color): 4 print('我是构造函数...') 5 self.name = name 6 self.age = age 7 self.color = color 8 9 def __del__(self):10 print('我是析构函数...')11 12 def func(self):13 print('我是func函数...')


__str__(self)函数:

如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。

对象实例化之后将数据给到对象名,此时如果打印对象名,在控制台上我们看到的是整个对象的类型以及在内存中的地址(十六进制),但是我们在开发过程中,对于类型和地址并不关注;我们更希望看到的是对象的各个属性内容,此时我们可以自己重新定义__str__(self)函数的函数体(就是函数重写),此函数有返回值,return后面的内容必须是str类型。

执行时机:在打印对象名/引用名时被触发。


# 没有定义__str__方法时,打印创建的对象 2 class Person(object): 3 4 def __init__(self,name,age,address): 5 self.name = name 6 self.age = age 7 self.address = address 8 9 p = Person('韩梅梅',20,'上海')10 print(p) # <__main__.Person object at 0x0000000004C3D978>11 12 13 # 有定义__str__方法时,打印创建的对象14 class Person(object):15 16 def __init__(self,name,age,address):17 self.name = name18 self.age = age19 self.address = address20 21 def __str__(self):22 return '姓名为:%s,年龄为:%d,籍贯是:%s人' %(self.name,self.age,self.address) # return后面必须是字符串数据23 24 p = Person('韩梅梅',20,'上海')25 print(p) # 姓名为:韩梅梅,年龄为:20,籍贯是:上海人


__dict__方法:

这个方法是以字典的形式列出类或对象中的所有成员,在类里面有,在对象里面也有。


1 class abc: 2 def __init__(self,age): 3 self.age=age 4 def __add__(self,obj): 5 return self.age+obj.age 6 7 a1=abc(18) 8 9 print(abc.__dict__) # 类里面的所有成员{'__add__': <function abc.__add__ at 0x0000020666C9E2F0>, '__module__': '__main__', '__weakref__': 10 # <attribute '__weakref__' of 'abc' objects>, '__init__': <function abc.__init__ at 0x0000020666C9E268>, '__doc__': None, 11 # '__dict__': <attribute '__dict__' of 'abc' objects>}12 13 print(a1.__dict__) # 对象里的成员{'age': 18}


面向对象的三大特性:封装性、继承性、多态性

1)封装(Encapsulation):

将内容封装到某个地方,以后再去调用被封装在某处的内容。 对于面向对象的封装来说,其实就是使用构造方法将内容封装到 对象 中,然后通过对象直接或者self间接获取被封装的内容。

计算机层面:

①模块、类、函数...②属性数据的封装与隐藏 (数据私有化);

封装的好处:安全性提高了。

场景演示:


1 class Person: 2 def __init__(self,name,age,money): 3 self.name = name 4 self.age = age 5 self.money = money 6 def __str__(self): 7 return "name:%s,age:%s,money:%s"%(self.name,self.age,self.money) 8 9 # 实例化Person对象10 p = Person('tom',30,10000)11 print(p) # name:tom,age:30,money:1000012 13 # age可以设置值,但是不符逻辑了14 p.age=-40 15 print(p) # name:tom,age:-40,money:10000


以上情况不会出现编译和运行异常,但是出现了数据不符合逻辑的情况;关系到对象直接在外部去操作数据(属性),导致"脏数据"的出现;要解决此问题,需要将上面的年龄age私有化,一旦私有化age之后,那么age使用的方位只有在class中,出了class外界无法使用他,使用__属性名的方式。

1)首先第一步是在外界不允许对象直接操作/访问属性(将此权利没收)--> 将属性私有化:__属性名。

2)需要在类的内部提供给外界额外的访问方式(函数:getter/setter)。


1 class Person: 2 def __init__(self,name,age,money): 3 self.name = name 4 self.__age = age 5 self.money = money 6 def __str__(self): 7 return "name:%s,age:%s,money:%s"%(self.name,self.__age,self.money) 8 9 # 实例化Person对象10 p = Person('tom',30,10000)11 print(p) # name:tom,age:30,money:1000012 13 p.age=-40 # 现在此行代码相当于动态为对象p添加一个属性age 14 print(p) # name:tom,age:30,money:10000,年龄任然是3015 16 # 查看对象p中所有的成员变量(属性)17 print(p.__dict__) # 得到{'name': 'tom', '_Person__age': 30, 'money': 10000} 18 '''所以age私有化之后,在计算机底层真正的名字已经变成了_Person__age,一个属性一旦被私有化,在底层真正的名字是:_类名__属性名19 其实python的私有化我们可以理解为伪私有(只是换了个名)'''20 21 # 以下的操作仅仅是为对象p动态添加一个属性为__age22 p.__age=-50 23 print(p) # 仍然得到name:tom,age:30,money:1000024 print(p.__dict__) # 再看属性{'name': 'tom', '_Person__age': 30, 'money': 10000, '__age': -50} ,只是多了一个名为__age的参数25 26 '''但是动态数据还是可以改的(但是不要去改,这样私有化就没意义了)'''27 p._Person__age = -10028 print(p) # 得到name:tom,age:-100,money:10000


私有化之后可以不会出现逻辑不符的现象,但是对于age,需要在类的内部提供给外界额外的访问方式(函数:getter/setter);

格式:get属性名(self)-->有返回值;set属性名(self,变量参数)-->有返回值;

以上两个函数的属性名都满足首字母大写其余字母小写的规范。


1 class Person: 2 def __init__(self,name,age,money): 3 self.name = name 4 self.__age = age 5 self.money = money 6 7 # 设置__age 8 def setAge(self,age): 9 # 对age值进行合法性的校验10 if age < 0 or age > 130:11 raise Exception('年龄不合法...')12 else:13 self.__age = age14 15 # 获取__age16 def getAge(self):17 return self.__age
18 def __str__(self):19 return "name:%s,age:%s,money:%s"%(self.name,self.__age,self.money)20 21 p = Person('tom',30,10000)22 print(p) # name:tom,age:30,money:1000023 24 # 调用函数完成设置和获取属性值的操作25 print(p.getAge()) # 3026 p.setAge(40)27 print(p) # name:tom,age:40,money:1000028 29 p.setAge(-40)30 print(p) # Exception: 年龄不合法...


总结:python的类中只有私有成员和公有成员两种,不像c++中的类有公有成员(public),私有成员(private)和保护成员(protected).并且python中没有关键字去修饰成员,默认python中所有的成员都是公有成员,但是私有成员是以两个下划线开头的名字标示私有成员,私有成员不允许直接访问,只能通过内部方法去访问,私有成员也不允许被继承。

在类的内部提供外界额外的访问方式(定义setter和getter方法),并且在需要的时候,可以在函数的内部加入数据合法性的校验;模板:对于setter函数,命名:set属性名(首字母大写);对于getter函数,命名:get属性名(首字母大写)。

2)继承性(Inheritance):

面向对象中的继承和现实生活中的继承相同,即:子可以继承父的内容。计算机层面:两部分组成,一部分我们称为父类(基类、超类、superclass);另一部分我们称为子类(派生类、subclass);子类可以使用父类中的成员(使用权)。

继承性的好处:

1)代码复用性变强;2)代码扩展性变强;3)代码维护性变好;4)代码阅读性变好;

继承性弊端:

类和类之间是一种强耦合关系,继承的好处要远远多于弊端,所以我们还是要经常使用继承的(合理),但不能为了继承而继承。

继承体系可以很庞大(呈现树状结构图),越往上层的类,感觉越模糊,越不清晰越往下层的类,感觉越清晰,越具体,所以得出结论,开发过程中创建父类的可能性变低,子类实例化的可能性极高。

注意事项:

1)由于继承的特点,子类对象被实例化,但是可能需要为父类属性赋值,那么可以在子类的构造函数中显示的调用父类构造来实现;

2)记住:虽然父类构造被执行,但是它仅仅做的就是赋值这件事,内存中的对象只有子类对象一个。

分类:

1)单继承(单一继承);2)多重继承;3)多继承(很多语言是不合法的)。

① 单继承的使用:


# 父类 2 class Person: 3 def __init__(self,name,age): 4 print('我是Person类的构造函数。。。') 5 self.name = name 6 self.age = age 7 # 吃 8 def eat(self): 9 print('吃一个...')10 # 睡11 def sleep(self):12 print('睡一会...')13 14 # 子类15 class Teacher(Person):16 def __init__(self,name,age,salary):17 print('我是teacher类的构造函数。。。')18 self.salary = salary19 # 在子类构造函数中显示的调用其父类构造;调用父类构造函数的目的:父类的属性由父类自己赋值20 # 方法1:super(Teacher,self).__init__(name,age)21 # 方法2:super().__init__(name,age)22 # 方法3:调用父类,这种方式最好23 Person.__init__(self,name,age) 24 25 # 教学26 def teach(self):27 print('教书育人...')28 29 # 实例化子类对象30 t = Teacher('老郭',30,6000.0)31 # 调用属性32 print(t.name,t.age,t.salary)33 # 调用函数34 t.eat()35 t.sleep()36 t.teach()


② 多重继承的使用:


# 定义生物类: 2 class Creature: 3 def __init__(self,age): 4 self.age = age 5 6 def breath(self): 7 print('呼吸...') 8 9 # 定义动物类:10 class Animal(Creature):11 def __init__(self,age,name):12 self.name = name13 super().__init__(age)14 15 def eat(self):16 print('吃饭...')17 18 # 定义狗类19 class Dog(Animal):20 def __init__(self,age,name,color):21 self.color = color 22 Animal.__init__(self,age,name)23 24 def wangwang(self):25 print('犬吠...')26 27 def __str__(self):28 return "name:%s,age:%s,color:%s" %(self.name,self.age,self.color)29 30 d = Dog(3,'旺财','black')31 print(d) # 因为定义了__str__,即重写,所以可以直接看到属性,不然 print(d)得到的是打印d的地址而已,只有print(t.name,t.age,t.color),才能看到结果33 d.wangwang()34 d.eat()35 d.breath()


③ 多继承的使用:大白话就是一个子类可以调用多个父类:


# 定义Father类 2 class Father: 3 def __init__(self,money): 4 self.money = money 5 6 def drinking(self): 7 print('喝喝喝...') 8 9 # 定义Mother类10 class Mother:11 def __init__(self,faceValue):12 self.faceValue = faceValue13 def shopping(self):14 print('买买买...')15 16 # 定义Child类,同时继承Father和Mother类17 class Child(Father,Mother):18 def __init__(self,money,faceValue,work):19 self.work = work20 Father.__init__(self,money)21 Mother.__init__(self,faceValue)22 23 def playing(self):24 print('玩玩玩...')25 26 # 实例化子类对象27 child = Child(1000000,True,"语数外")28 29 # 调用函数30 child.playing() # 玩玩玩...31 child.drinking() # 喝喝喝...32 child.shopping() # 买买买...


函数重写(复写,覆盖,override) :

前提:必须有继承性;原因:父类中的功能(函数),子类需要用,但是父类中函数的函数体内容和我现在要执行的逻辑还不相符,那么可以将函数名保留(功能还是此功能),但是将函数体重构;注意:子类重写父类的函数,除了函数体以外的部分,直接复制父类的即可。


1 class Fu: 2 def func(self): 3 print('辟邪剑法...') 4 5 class Zi(Fu): 6 def func(self): 7 super().func() 8 print('葵花宝典...') 9 10 # 实例化子类对象11 zi = Zi() 12 13 zi.func() # 得到 辟邪剑法14 # 葵花宝典







推荐阅读

>>>新手必备-Linux入门之云计算是什么

>>>红帽认证入门-Linux系统介绍及企业版本选型

>>>新手必备-Linux系统安装配置+Xshell远程连接

>>>Linux常用命令行合集之绝对路径和相对路径

>>>软连接与硬连接



运维界升职加薪必备的云计算技术,你学了吗?

学完高级运维云计算课程之后,你可以:

  • 跨越90%企业的招聘硬门槛

  • 增加70%就业机会

  • 拿下BAT全国TOP100大厂敲门砖

  • 体系化得到运维技术硬实力

  • 技术大佬年薪可达30w+