Python自动化运维之路Day8基础篇之面向对象下篇
Posted DBQ
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python自动化运维之路Day8基础篇之面向对象下篇相关的知识,希望对你有一定的参考价值。
今日目录:
类成员
类成员修饰符
特殊的类成员
面向对象相关联的其他知识
异常捕获与处理
设计模式之单例模式
一. 类成员
类的成员有:字段、方法和属性
关系图如下:
1. 字段:
字段分:
- 静态字段
- 普通字段
两者在定义和使用上有所区别,如下代码:
class Province: contry = \'中国\' #静态字段,保存在类中 def __init__(self,name): self.name = name #普通字段,保存在对象中
在内存中的存储位置是不同的, 静态字段保存在类中, 而普通字段保存在对象中。调用字段:
class Province: contry = \'中国\' def __init__(self,name): self.name = name sx = Province(\'山西\') #访问静态字段和普通字段 # 静态字段存储在类中, 使用类调用, 如 Province.contry # 普通字段存储在对象中, 由对象调用, 如 sx.name print(\'国家: %s\\n省份: %s\'%(Province.contry,sx.name)) #执行结果: 国家: 中国 省份: 山西
可以看出:
静态字段存储在类中, 只在内存中保存一份;
普通字段存储在每个对象中,需要在每个对象中都保存一份
所以,当类创建时,如果每个对象都具有相同的字段,就使用静态字段。
2. 方法
包括:
- 普通方法,由对象调用,至少需要一个self参数。执行普通方法时,self就是对象本身,自动将调用该方法的对象赋值给self
- 静态方法,属于类,由类来调用执行,无默认参数,等同于函数。创建方式: @staticmethod。
- 类方法,是静态方法的一种特殊形式。由类调用,至少要有一个参数cls,值为类名。创建方式: @classmethod
所有的方法都属于类,只不过调用方式不同。
class Province: #静态字段 country = \'中国\' def __init__(self,arg): self.arg = arg #普通方法 def show(self): print(\'普通方法,由对象调用,至少需要一个self参数。执行普通方法时,self就是对象本身,自动将调用该方法的对象赋值给self!\') #静态方法 @staticmethod def static_show(): print(\'静态方法,属于类,由类来调用执行,无默认参数。创建方式: @staticmethod\') @classmethod def class_show(cls): print(\'是静态方法的一种特殊形式。由类调用,至少要有一个参数cls,值为类名。创建方式: @classmethod\') # print(cls) #调用静态方法,由类调用 Province.static_show() #调用类方法, 由类调用 Province.class_show() #调用普通方法,需要先实例化 obj = Province(\'山西\') obj.show() #执行结果: 静态方法,属于类,由类来调用执行,无默认参数。创建方式: @staticmethod 是静态方法的一种特殊形式。由类调用,至少要有一个参数cls,值为类名。创建方式: @classmethod 普通方法,由对象调用,至少需要一个self参数。执行普通方法时,self就是对象本身,自动将调用该方法的对象赋值给self! #类方法的参数cls是类本身,也可以在类方法里操作, 如下: @classmethod def class_show(cls): # print(\'是静态方法的一种特殊形式。由类调用,至少要有一个参数cls,值为类名。创建方式: @classmethod\') a = cls(\'核弹\') a.static_show() #执行结果: 静态方法,属于类,由类来调用执行,无默认参数。创建方式: @staticmethod
相同点:由于所有的方法都属于类, 所以在内存中存储只保存一份。
不同点:由于各种方法的调用方式不同,调用方法时自动传入的参数不同。
但是有个比较刺激的, 继续上面的例子:
#直接在对象中调用静态方法和类方法 obj = Province(\'山西\') obj.show() obj.static_show() obj.class_show() #执行结果: 普通方法,由对象调用,至少需要一个self参数。执行普通方法时,self就是对象本身,自动将调用该方法的对象赋值给self! 静态方法,属于类,由类来调用执行,无默认参数。创建方式: @staticmethod 是静态方法的一种特殊形式。由类调用,至少要有一个参数cls,值为类名。创建方式: @classmethod
so,也是可以被调用的,虽说可以调用,但是不到万不得已,不要用对象来直接调用原属于类该调用的东西。这就是要谈到的要遵循的编程规则了:
一般情况下,自己访问自己的字段,虽说Python没有做严格的限制,但是这是理应遵循的一种编程习惯。
3. 属性
访问属性时和访问字段时一样,不需要加()。 在Python中,属性其实就是普通方法的变种
(1) 基本使用
class C1: xxx = \'静态字段\' def func(self): print(\'普通方法\') #定义一个属性,使用 @property装饰 @property def f1(self): print(\'属性\') #实例化 obj = C1() #调用普通字段,用类调用 C1.xxx #调用普通方法, 需要() obj.func() #调用定义的属性,直接用调用字段的方式,不用加() obj.f1 #代码执行结果: 普通方法 属性
class C1: xxx = \'静态字段\' def func(self): print(\'普通方法\') #定义一个属性,使用 @property装饰 @property def f1(self): return \'属性\' #实例化 obj = C1() #调用普通字段,用类调用 C1.xxx #调用普通方法, 需要() obj.func() #调用定义的属性,直接用调用字段的方式,不用加() result = obj.f1 #如果有return值,需要赋值给变量,而后调用 print(result)
属性的定义和调用要注意:
定义时,在普通方法上面加 @property 装饰器来装饰
定义时,属性中仅有一个self参数,不能有其他的
调用的时候, 不需要()
属性存在意义是: 访问属性时可以制造出和访问字段相同的假象
属性是普通方法变种而来,如果Python中没有属性,方法完全可以代替其功能。
实例: 对于主机列表页面,每次请求不可能把数据库中的所有的内容显示到页面上,而是通过分页的功能显示,所以,在向数据库请求数据时就需要显式指定获取从第m条到第n条的数据(limit m,n), 这个分页的功能包括:
- 根据用户请求的当前页和总数据条数计算出m和n
- 根据m和n去数据库中请求数据
class Pager: def __init__(self,curr_num,per_page): \'\'\' 构造函数 :param curr_num: 当前请求的页码 :param per_page: 每页要显示的数据 :return: \'\'\' self.curr_num = curr_num self.per_page = per_page #计算出开始位 @property def start(self): #-1就是找出上一页开始位置 value = (self.curr_num-1) * self.per_page return value #计算出结束位 @property def end(self): value = self.curr_num * self.per_page return value #实例化,传入请求页码和每页显示的数量 #要显示哪页,传入参数即可 P = Pager(1,10) #起始值 print(P.start) #结束值 print(P.end) #执行结果 0 10
(2) 属性的两种定义方式
a. 装饰器方式:
在普通方法的上面添加 @property装饰器的方式
Python中的类有经典类和新式类(python3中全是新式类),新式类的类型比经典类丰富。(如果类继object,那么该类是新式类)
经典类,有一种 @property装饰器
class Pager: def __init__(self,curr_num,per_page): \'\'\' 构造函数 :param curr_num: 当前请求的页码 :param per_page: 每页要显示的数据 :return: \'\'\' self.curr_num = curr_num self.per_page = per_page #计算出开始位 @property def start(self): #-1就是找出上一页开始位置 value = (self.curr_num-1) * self.per_page return value #计算出结束位 @property def end(self): value = self.curr_num * self.per_page return value #实例化,传入请求页码和每页显示的数量 P = Pager(1,10) #起始值 print(P.start) #自动执行 @property装饰的start方法 #结束值 print(P.end) #自动执行 @property装饰的end方法
新式类,有三种 @property装饰器,类似于在字典中我们的操作,如:
a = {\'k1\':\'v1\'} print(a.get(\'k1\')) #获取值 a[\'k1\'] = \'v2\' #设置一个值 del a[\'k1\'] #删除一个key print(a)
class Pager: def __init__(self,curr_num,per_page): \'\'\' 构造函数 :param curr_num: 当前请求的页码 :param per_page: 每页要显示的数据 :return: \'\'\' self.curr_num = curr_num self.per_page = per_page #计算出开始位 @property def start(self): #-1就是找出上一页开始位置 value = (self.curr_num-1) * self.per_page return value @start.setter def start(self,value): # value = self.curr_num * self.per_page print(\'你输入的参数是: %s ,这是新修改的值\'%value) #return value @start.deleter def start(self): print(\'这里是删除dele动作,你触发了del操作\') #实例化,传入请求页码和每页显示的数量 P = Pager(1,10) #起始值 result = P.start #自动执行 @property装饰的start方法 print(result) P.start = \'shayashi\' #自动执行 @start.setter装饰的start方法 del P.start #自动执行 @start.deleter装饰的start方法 #执行结果: 0 你输入的参数是: shayashi ,这是新修改的值 这里是删除dele动作,你触发了del操作
经典类中的属性只有一种访问方式,对应的是被 @property装饰的方法
新式类中的属性有三种, @property, @方法名.setter, @方法名.deleter
b. 静态字段方式
创建值为 property对象的字段, 这种方式经典类和新式类无区别。
class Test: def __init__(self,name): self.name = name def show(self): return self.name xx = property(show) #注意方法这里不能加(),否则报错 obj1 = Test(\'daniel\') result = obj1.xx #自动会调用 show方法,并获取返回值 print(result)
property构造方法的四个参数:
- 第一个是fget,值是方法名,调用 对象.属性 属性时自动触发执行
- 第二个是fset,值是方法名,调用 对象.属性=值 的时候自动触发执行
- 第三个是fdel,值是方法名,调用 del 对象.属性 的时候自动触发执行
- 第四个参数是doc, 调用 对象.属性.__doc__ 的时候自动打印,这是该属性的描述信息
以上四种方式中的参数都是可选,可以直接写方法名,但是注意的是,直接写方法名的时候,要对应好位置,比如要写del的一个属性,必须要加上前两个。
class Test: def __init__(self,name): self.name = name def show(self): return self.name def setter(self,value): print(\'setter\') def deleter(self): print(\'deleter\') # xx = property(show,setter,deleter,\'这是一段描述信息\') #注意方法这里不能加(),否则报错 xx = property(fget=show,fdel=deleter) #如果只定义一个get和del的属性,那么就需要使用fget和fdel显式指定了,用普通方法名已经不能满足需求了 obj1 = Test(\'daniel\') result = obj1.xx #自动会调用 show方法,并获取返回值 # obj1.xx = \'xxoo\' #自动会调用 setter方法,并获取返回值 del obj1.xx #自动会调用 deleter 方法,并获取返回值 print(result)
二. 类成员修饰符
上面介绍了类的成员,这会来看下类成员的修饰符,顾名思义,作用就是来修饰类成员的。 对于每一个类的成员,都有两种形式:
- 公有成员,在任何地方都可以访问
- 私有成员,只允许在类的内部访问
私有成员和共有成员的定义不同,私有成员命名时,前两个字符是下划线(特殊成员除外,如__init__, __call__等)。
class Foo: country = \'中国\' #公有静态字段 __xx = \'不给你看!\' #私有静态字段 def __init__(self,name): self.name = name #共有普通字段 self.__oo = \'还不让你看!\' #私有普通字段
私有成员和公有成员的访问限制不同:
1. 字段
静态字段:
- 共有静态字段: 类可以访问; 类内部可以访问; 子类中可以访问。
- 私有静态字段: 仅类内部可以访问class Foo:
country = \'中国\' #公有静态字段 __xx = \'不给你看!\' #私有静态字段 def __init__(self,name): self.name = name #共有普通字段 self.__oo = \'还不让你看!\' #私有普通字段 def test(self): print(self.__xx) def test1(self): print(self.__oo) #静态字段: print(Foo.country) #从类里访问静态公共字段,成功 #Foo.__xx #访问私有静态字段, 失败 obj = Foo(\'dbq\') #在类里面访问静态字段, 成功 obj.test() obj.test1() #在类里面访问普通私有字段,成功
class Foo: country = \'中国\' #公有静态字段 __xx = \'不给你看!\' #私有静态字段 def __init__(self): self.__oo = \'还不让你看!\' #私有普通字段 def test(self): print(Foo.__xx) def test1(self): print(self.__oo) class Foo1(Foo): def test2(self): print(Foo.country) #子类访问父类公共静态字段, 成功 print(Foo.__xx) #子类访问父类私有静态字段, 错误 obj1 = Foo1() obj1.test2()
普通字段:
- 公共普通字段: 对象可以访问, 类内部可以访问,子类(派生类) 中可以访问;
- 私有普通字段: 仅能从类内部访问;
class Foo: country = \'中国\' #公有静态字段 __xx = \'不给你看!\' #私有静态字段 def __init__(self): self.__oo = \'私有普通字段,不让你看!\' #私有普通字段 self.name = \'DBQ\' def test(self): print(self.name) def test1(self): print(self.__oo) class Foo1(Foo): def test2(self): print(self.name) obj1 = Foo() print(obj1.name) #通过对象访问, 成功 obj1.test() #类内部访问, 成功 obj2 = Foo1() obj2.test2() #子类中访问,成功 #执行结果: DBQ DBQ DBQ
class Foo: country = \'中国\' #公有静态字段 __xx = \'不给你看!\' #私有静态字段 def __init__(self): self.__oo = \'私有普通字段,不让你看!\' #私有普通字段 self.name = \'DBQ\' def test(self): print(self.__oo) def test1(self): print(self.__oo) class Foo1(Foo): def test2(self): print(self.__oo) obj1 = Foo() print(obj1.__oo) #通过对象访问, 失败 obj1.test() #类内部访问, 成功 obj2 = Foo1() obj2.test2() #子类中访问, 失败
注意, 如果要强制访问私有字段,可以通过 [ 对象._类名__私有字段名 ] 来访问,但是强烈不建议强制访问私有成员!
class Foo: country = \'中国\' #公有静态字段 __xx = \'不给你看!\' #私有静态字段 def __init__(self): self.__oo = \'私有普通字段,不让你看!\' #私有普通字段 self.name = \'DBQ\' def test(self): print(self.__oo) def test1(self): print(self.__oo) class Foo1(Foo): def test2(self): print(self.__oo) obj1 = Foo() print(obj1._Foo__oo) #通过 [对象名._类名__私有字段名] 强制访问成功 #虽说能成功,但是不建议这么访问私有字段!
2. 方法
普通类方法
- 公共普通类方法:对象可以访问, 子类可以访问
- 私有普通类方法:仅能从类内部访问
class Foo: def show(self): print(\'这是公共普通方法\') class Son(Foo): def show1(self): print(self.show()) obj = Foo() obj.show() #对象访问, 成功 # Foo.show() #类调用,失败 obj_son = Son() obj_son.show1() #子类访问, 成功
class Foo: def __show(self): print(\'这是私有静态方法\') def show2(self): self.__show() class Son(Foo): def show1(self): print(self.__show()) obj = Foo() #obj.__show() #外部对象访问, 失败 obj.show2() #类内部访问, 成功 #Foo.show2() #类调用,失败 obj_son = Son() obj_son.show1() #子类访问, 失败
静态类方法
- 公共静态类方法:对象可以访问, 类内部可以访问,子类(派生类) 中可以访问;
- 私有静态类方法:仅能从类内部访问
class Foo: @staticmethod def show(): print(\'这是共有静态类方法\') def show2(self): self.show() class Son(Foo): def show1(self): print(self.show()) obj = Foo() obj.show() #外部对象访问, 成功 obj.show2() #类内部访问, 成功 obj_son = Son() obj_son.show1() #子类访问, 成功
class Foo: @staticmethod def __show(): print(\'这是私有静态类方法\') def show2(self): self.以上是关于Python自动化运维之路Day8基础篇之面向对象下篇的主要内容,如果未能解决你的问题,请参考以下文章