Python之路系列:面向对象初级:静态属性、静态方法、类方法

1、静态属性

静态属性至关于数据属性。html

用@property语法糖装饰器将类的函数属性变成能够不用加括号直接的相似数据属性。python

能够封装逻辑,让用户感受是在调用一个普通的数据属性。linux

例子:算法

 

class Room: def __init__(self,name,length,width,heigh): self.name = name self.length = length self.width = width self.heigh = heigh ​ @property #经过使用property语法糖装饰器,使类的函数属性返回相似数据属性,不用使用括号。
    def cacl_area(self): return self.length*self.width ​ ​ r1 = Room("nick",20,30,10) print(r1.cacl_area)

 


 


 

 

2、类方法

需求:类不经过实例(对象)直接调用类的函数属性。网络

类没法直接调用本身的函数属性,须要借助实例对象。ide

例子:函数

class Room: def __init__(self,name,length,width,heigh): self.name = name self.length = length self.width = width self.heigh = heigh ​ def cacl_area(self): return self.length*self.width ​ def tell_info(self): print("-----") ​ Room.tell_info() #这样直接用类调用函数属性会出错,须要实例对象
r1=Room("nick",10,10,10) r1.tell_info()

须要用@classmethod ,将函数作成类方法,执行类方法时,自动将调用该方法的复制给cls工具

例子:spa

class Room: def __init__(self,name,length,width,heigh): self.name = name self.length = length self.width = width self.heigh = heigh ​ def cacl_area(self): return self.length*self.width ​ @classmethod def tell_info(cls,msg): print(cls) print("-----",msg) ​ Room.tell_info("hello")  # 至关于Room.tell_info(Room,"hello"),classmethod自动将类传入了

 


 


 

 

3、静态方法

用@staticmethod 语法糖实现,不和具体的实例对象进行绑定,类能够调用实例也能够调用设计

例子:

class Room: def __init__(self,name,length,width,heigh): self.name = name self.length = length self.width = width self.heigh = heigh ​ def cacl_area(self): return self.length*self.width ​ @staticmethod def test(a,b,c): print(a,b,c) ​ ​ Room.test("hello","world","!!!")  #静态方法类能够调用,实例也能够调用
r1 = Room("nick",20,30,10) r1.test("nick","hello","!!!")

 

 

输出结果:

hello world !!!
nick hello !!!

 

静态方法只是名义上归属类管理,不能使用类变量和实例变量,是类的工具包。

补充:

绑定方法说明:

  ♦绑定方法(绑定给谁,谁来调用就自动将它自己看成第一个参数传入)

  ♦绑定到对象的方法:没有被任何装饰器装饰的方法。

  ♦绑定给类的方法(classmethod)

  ♦非绑定方法:用staticmethod装饰器装饰的方法:不与类或对象绑定,类和对象均可以调用,可是没有自动传值。


 


 

 

4、组合

组合指的是,在一个类中以另一个类的对象做为数据属性,称为类的组合

用途:

  一、不一样的类作关联

  二、不一样的类组合成大的总体。

例子1:

class School: def __init__(self,name,addr,type): self.name = name self.addr = addr self.type = type ​ ​ def enrol_students(self): print("%s正在招生"%self.name) ​ ​ def exam(self): print("%s正在考试"%self.name) ​ ​ class Course: ​ ​ def __init__(self,name,price,period,school): self.name = name self.price = price self.period = period self.school = school ​ ​ s1 = School("清华","北京","公立") s2 = School("北大","北京","公立") ​ school_dic = { "1":s1, "2":s2 } ​ msg = """ 请选择学校 一、清华 二、北大 ​ """
while True: print(msg) school_choice = input(">>>: ").strip() name = input("课程名:").strip() price = input("课程费用:").strip() period = input("课程周期:").strip() school_obj = school_dic[school_choice] c1 = Course(name,price,period,school_obj)  #直接传入学校的对象,不一样类之间创建了关联
    print("%s属于%s"%(c1.name,c1.school.name))

 

 

例子2:

class Head(): pass ​ ​ class Trunk: pass ​ ​ class Hand(): pass ​ ​ class Foot(): pass ​ ​ class People(): def __init__(self, name, age, gerder, ): self.name = name self.age = age self.gerder = gerder self.head = Head()  # 人的头继承了head类这个类得实例,这里是一个对象
        self.trunk = Trunk() self.hand = Hand() self.foot = Foot() ​ ​ p1 = People("nick", '18', "man") print(p1.__dict__)  # 打印获得一个属性head ->>> key的值对应的head实例

 

例子3:

rom math import pi ​ class Circle: def __init__(self,radius): self.radius = radius ​ ​ def cacl_area(self): print("圆的面积是%s"%(pi*self.radius*self.radius)) return pi*self.radius*self.radius ​ ​ class Ring: def __init__(self,outside_radius,inside_radius): self.outside_circle = Circle(outside_radius)  #获取外圈圆的对象
        self.inside_circle = Circle(inside_radius)    #获取内圈圆的对象
​ ​ def area(self): ring_area = self.outside_circle.cacl_area() - self.inside_circle.cacl_area() print("圆环的面积是%s"%ring_area) ​ ​ r1 = Ring(10,8) r1.area()

 

 

5、继承

什么是继承?


 

  继承指的是类与类之间的关系,是一种什么“是”什么的关系,继承是一种建立新类的方式,新建的类能够继承一个或多个父类(python支持多继承),父类又可称为基类或超类,新建的类称为派生类或子类。

子类会“”遗传”父类的属性,从而解决代码重用问题。

例子1:

class Parent_class: money = 10
    def __init__(self,name): self.name = name ​ def make_money(self): print("make good money") ​ ​ class Sub_class(Parent_class): pass ​ s1 = Sub_class("nick")  #这里须要传入一个参数,虽然子类没有默认的__init__方法,子类找不到去父类找,父类须要一个参数
print(s1.money) #这里直接调用父类的数据属性
s1.make_money() #调用父类的函数属性

 

 

分析:子类继承了父类的数据属性和函数属性

 

派生


 

  子类也能够添加本身新的属性或者在本身这里从新定义这些属性(不会影响到父类),须要注意的是,一旦从新定义了本身的属性且与父类重名,那么调用新增的属性时,就以本身为准了。

 

例子2:

class Parent_class: money = 10
    def __init__(self,name): self.name = name ​ def make_money(self): print("make good money") ​ ​ class Sub_class(Parent_class): money =  10000 ​ s1 = Sub_class("nick") print(s1.money) #这里子类有本身的与父类同名的属性,从子类的属性开始找,本身有就用本身的数据,再也不用父类的数据
print(Parent_class.money) #父类的属性任然存在,而不是被子类的同名属性覆盖了
s1.make_money() #调用父类的函数属性

 

分析:子类也能够添加本身新的属性或者在本身这里从新定义这些属性(不会影响到父类),须要注意的是,一旦从新定义了本身的属性且与父类重名,那么调用新增的属性时,就以本身为准了。父类的属性任然存在。

 

在子类中,新建的重名的函数属性,在编辑函数内功能的时候,有可能须要重用父类中重名的那个函数功能,应该是用调用普通函数的方式,即:类名.func(),此时就与调用普通函数无异了,所以即使是self参数也要为其传值。

class Parent_class1: money = 10
    def __init__(self,name): self.name = name ​ def make_money(self): print("from Parent_class1") print("make good money") ​ ​ class Sub_class(Parent_class1,): money =  10000def make_money(self):  #在本身这里定义新的make_money,再也不使用父类的make_money,且不会影响父类
        Parent_class1.make_money(self)   #若是想调用父类的同名函数属性,这里要本身手写self。
        print("from Sub_class") ​ ​ s1 = Sub_class("nick") s1.make_money()

 

属性查找


 

属性引用查找,会先从对象中找,而后去类中找,而后再去父类中找...直到最顶级的父类。

例子:

class Parent_class1: def f1(self): print("from Parent_class f1") ​ def f2(self): self.f1() print("from Parent_class f2") ​ ​ class Sub_class(Parent_class1,): def f1(self): print("from Sub_class") ​ ​ s1 = Sub_class() s1.f2()

 

输出结果:

from Sub_class from Parent_class f2

 

 

分析:对象s1本身的类自己没有f2()函数属性,到父类找,找到了执行self.f1(),这里的self是对象s1,因此s1.f1()函数属性执行结果是输出from Sub_class,而后执行print("from Parent_class f2")输出from Parent_class f2

 

查看继承


 

__base__、__bases__方法

class Parent_class1: money = 10
    def __init__(self,name): self.name = name ​ def make_money(self): print("make good money") ​ class Parent_class2: passclass Sub_class(Parent_class1,Parent_class2): money =  10000 ​ ​ print(Sub_class.__base__) print(Sub_class.__bases__)

 

输出结果:

<class '__main__.Parent_class1'> (<class '__main__.Parent_class1'>, <class '__main__.Parent_class2'>)

 

分析:

  
#__base__只查看从左到右继承的第一个子类,__bases__则是查看全部继承的父类

何时用继承?


 

  • 当类之间有不少相同的功能,提取这些共同的功能作成基类,用继承比较好。

    经过继承创建了派生类与基类之间的关系,它是一种'是'的关系,好比白马是马,人是动物。

    好比老师是人,学生是人。

  • 当类之间有显著不一样,而且较小的类是较大的类所须要的组件时,用组合比较好。

    用组合的方式创建了类与组合的类之间的关系,它是一种‘有’的关系,好比教授有生日,教授教python和linux课程,教授有学生s一、s二、s3。


     


     

6、接口继承和统一化设计

 

继承同时具备两种含义:

  • 继承基类的方法,而且作出本身的改变或者扩展(解决代码重用问题)。

  • 声明某个子类兼容于某基类,定义一个接口类,子类继承接口类,而且实现接口中定义的方法。

继承的第二种含义很是重要。它又叫“接口继承”。

封装:封装的意义,在于明确标识出容许外部使用的全部成员函数和数据项,或者叫接口。

真正的封装是,通过深刻的思考,作出良好的抽象,给出“完整且最小”的接口,并使得内部细节能够对外透明(注意:对外透明的意思是,外部调用者能够顺利的获得本身想要的任何功能,彻底意识不到内部细节的存在

 

总结


 

 (1)实践中继承的第一种含义意义并非很大,甚至经常是有害的。由于它使得子类与基类出现强耦合。

 (2)继承的第二种含义很是重要。接口继承实质上是要求“作出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的全部对象”,这在程序设计上叫作归一化。

归一化使得高层的外部使用者能够不加区分的处理全部接口兼容的对象集合——就好象linux的泛文件概念同样,全部东西均可以当文件处理,没必要关心它是内存、磁盘、网络仍是屏幕。

​ 接口提取了一群类共同的函数,能够把接口当作一个函数的集合。而后让子类去实现接口中的函数。归一化,就是只要是基于同一个接口实现的类,那么全部的这些类产生的对象在使用时,从用法上来讲都同样。归一化,让使用者无需关心对象的类是什么,只须要的知道这些对象都具有某些功能就能够了,这极大地下降了使用者的使用难度。

 

抽象类


 

抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化。

若是说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。

  好比咱们有香蕉的类,有苹果的类,有桃子的类,从这些类抽取相同的内容就是水果这个抽象的类,你吃水果时,要么是吃一个具体的香蕉,要么是吃一个具体的桃子。。。。。。你永远没法吃到一个叫作水果的东西。

​ 从设计角度去看,若是类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。

  从实现角度来看,抽象类与普通类的不一样之处在于:抽象类中只能有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法。

 

抽象类与接口


 

抽象类的本质仍是类,指的是一组类的类似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的类似性。

*抽象类是一个介于类和接口直接的一个概念,同时具有类和接口的部分特性,能够用来实现归一化设计*

 

接口继承的使用例子(抽象类)


 

import abc class All_file(metaclass=abc.ABCMeta):   # 接口类(基类),接口类的方法不须要具体实现,也不须要实例化
​ @abc.abstractmethod #引入第三方模块abc,调用abc下的abstractmethod,强制子类下必须有read方法
    def read(self): pass ​ @abc.abstractmethod #强制子类下必须有write方法
    def write(self): pass ​ ​ class Text_file(All_file): def read(self): print("from text-file read") ​ def write(self): print("from text-file write") ​ ​ class Disk(All_file): def read(self): print("from disk read") ​ def write(self): print("from disk write") ​ class Cdrom(All_file): def read(self): print("from cdrom read") ​ def write(self): print("from cdrom write") ​ f1 = Disk() f1.read()

 


 


 

 

7、继承顺序

经典类与新式类


 

在python2中,新式类基类继承object类,经典类不继承任何类

\

  默认都是经典类,只有显式继承了object才是新式类,即:

class Person(object):pass 新式类写法

class Person():pass 经典类写法

class Person:pass 经典类写

 

在Python 3.x中取消了经典类,默认都是新式类,而且没必要显式的继承object

  
class Person(object):pass
class Person():pass
class Person:pass
三种写法并没有区别

 

继承顺序


 

Python中子类能够同时继承多个父类,如A(B,C,D)

若是继承关系为非菱形结构,则会按照先找B这一条分支,而后再找C这一条分支,最后找D这一条分支的顺序直到找到咱们想要的属性。

深度优先和广度优先


 

继承关系为菱形结构,那么属性的查找方式有两种,分别是:深度优先和广度优先。

 

 例子:

class G: def test(self): print("from G") ​ class F(G): def test(self): print("from F") ​ class E(G): def test(self): print("from E") ​ ​ class D(G): def test(self): print("from D") ​ class C(F): def test(self): print("from C") ​ class B(E): def test(self): print("from B") passclass A(B,C,D): def test(self): print("from A") # pass
​ c1 = A() c1.test() ​ # python3环境中,新式类继承的寻找的顺序是A-B-E-C-F-D-G #在python2环境中,这个为经典类,继承寻找的顺序为A-B-E-G-C-F-D #python3中统一都是新式类,所有按照广度优先继承 #pyhon2中才分新式类与经典类

 

 

 

继承原理


 

对于定义的每个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的全部基类的线性顺序列表。

为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。而这个MRO列表的构造是经过一个C3线性化算法来实现的。

C3线性化算法实际上就是合并全部父类的MRO列表并遵循以下三条准则:

  • 子类会先于父类被检查

  • 多个父类会根据它们在列表中的顺序被检查

  • 若是对下一个类存在两个合法的选择,选择第一个父类

例子(python3)

class G: def test(self): print("from G") ​ class F(G): def test(self): print("from F") ​ class E(G): def test(self): print("from E") ​ class D(G): def test(self): print("from D") ​ class C(F): def test(self): print("from C") ​ class B(E): def test(self): print("from B") passclass A(B,C,D): def test(self): print("from A") ​ print(A.mro())

 

输出结果:

[<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class '__main__.G'>, <class 'object'>]

 

分析:#python2中的新式类也和python3同样,都有mro方法,python2中经典类没有mro方法。


 


 

 

8、子类调用父类方法

在子类派生出的新方法中,每每须要重用父类的方法,咱们有两种方式实现

第一种方式:直接调用


 

指名道姓,即父类名.父类方法()

class Vehicle:  #定义父类
def __init__(self,name,speed,power): self.name = name self.speed = speed self.power = power ​ def run(self): print("running !") ​ class Subway(Vehicle):  #定义子类
def __init__(self,name,speed,power,line): Vehicle.__init__(self,name,speed,power)  #这里调用父类的构造方法,直接用父类名.构造方法名(),
                                                 # 须要本身传入self和其余父类的参数
        self.line = line ​ def run(self): Vehicle.run(self) #这里调用父类的run()方法也是采用父类名.父类方法()的方式,也是须要本身传入self
        print("%s %s号线开动了"%(self.name,self.line)) ​ s = Subway("深圳地铁","70km/h","电力",1) s.run()

 

第二种方式:super()


 

用super().父类方法(),这种方式不用写父类的名字,不用再传入self这个参数了。

 

class Vehicle:  #定义父类
def __init__(self,name,speed,power): self.name = name self.speed = speed self.power = power ​ def run(self): print("running !") ​ class Subway(Vehicle):  #定义子类
def __init__(self,name,speed,power,line): super().__init__(name,speed,power)  #这里用super().父类的方法名调用父类的方法,这种方式不用传入self参数
        self.line = line ​ def run(self): super().run() #这里调用父类的run()方法也是采用super().父类方法()的方式,不须要本身传入self参数
        print("%s %s号线开动了"%(self.name,self.line)) ​ s = Subway("深圳地铁","70km/h","电力",1) s.run()

 转自连接

相关文章
相关标签/搜索