Python之路(第二十三篇) 面向对象初级:静态属性、静态方法、类方法

时间:2022-09-09 23:17:56

一、静态属性

静态属性相当于数据属性。

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

可以封装逻辑,让用户感觉是在调用一个普通的数据属性。

例子

  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)

  

二、类方法

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

类无法直接调用自己的函数属性,需要借助实例对象。

例子

  
  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

例子

  
  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自动将类传入了

  

三、静态方法

用@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装饰器装饰的方法:不与类或对象绑定,类和对象都可以调用,但是没有自动传值。

四、组合

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

用途:1、不同的类做关联

2、不同的类组合成大的整体。

例子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 = """
请选择学校
1、清华
2、北大

"""
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

  from 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()

  

 

五、继承

什么是继承?

继承指的是类与类之间的关系,是一种什么“是”什么的关系,继承是一种创建新类的方式,新建的类可以继承一个或多个父类(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 = 10000

def 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:
pass

class 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课程,教授有学生s1、s2、s3。

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

继承同时具有两种含义

  • 继承基类的方法,并且做出自己的改变或者扩展(解决代码重用问题)。

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

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

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

真正的封装是,经过深入的思考,做出良好的抽象,给出“完整且最小”的接口,并使得内部细节可以对外透明(注意:对外透明的意思是,外部调用者可以顺利的得到自己想要的任何功能,完全意识不到内部细节的存在。

总结

 (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()

  

七、继承顺序

经典类与新式类

在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这一条分支的顺序直到找到我们想要的属性。

深度优先和广度优先

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

Python之路(第二十三篇) 面向对象初级:静态属性、静态方法、类方法

Python之路(第二十三篇) 面向对象初级:静态属性、静态方法、类方法

Python之路(第二十三篇) 面向对象初级:静态属性、静态方法、类方法

Python之路(第二十三篇) 面向对象初级:静态属性、静态方法、类方法

例子

  
  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")
pass

class 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")
pass

class 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方法。

八、子类调用父类方法

在子类派生出的新方法中,往往需要重用父类的方法,我们有两种方式实现

第一种方式:直接调用

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

  
  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()

  

Python之路(第二十三篇) 面向对象初级:静态属性、静态方法、类方法的更多相关文章

  1. python面向对象之静态属性&sol;静态方法&sol;类方法&sol;组合

    继续学习,不要松懈 #!/usr/bin/env python # coding:utf-8 class Campus: def __init__(self,name,addr,type): self ...

  2. Python之路&lpar;第二十七篇&rpar; 面向对象进阶:内置方法、描述符

    一.__call__ 对象后面加括号,触发执行类下面的__call__方法. 创建对象时,对象 = 类名() :而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()( ...

  3. Python开发【第二十三篇】:持续更新中&period;&period;&period;

    Python开发[第二十三篇]:持续更新中...

  4. 面向对象:静态属性&comma;静态方法&comma;组合&comma;继承&comma;衍生&comma;继承之mro线性顺序列表&comma;面向对象综合实例

    1.静态属性(附有装饰器) class Room: def __init__(self,name,owner,width,length,height): self.name=name self.own ...

  5. Python之路&lpar;第二十一篇&rpar; re模块

    一.re模块 正则表达式本身是一种小型的.高度专业化的编程语言,正则表达式就是字符串的匹配规则,在多数编程语言里都有相应的支持,python里对应的模块是re,正则表达式模式被编译成一系列的字节码,然 ...

  6. Python之路&lpar;第十三篇&rpar;time模块、random模块、string模块、验证码练习

    一.time模块 三种时间表示 在Python中,通常有这几种方式来表示时间: 时间戳(timestamp) : 通常来说,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量.(从 ...

  7. Python之路&lpar;第二十篇&rpar; subprocess模块

    一.subprocess模块 subprocess英文意思:子进程 那什么是进程呢? (一)关于进程的相关理论基础知识 进程是对正在运行程序的一个抽象,进程的概念起源于操作系统,是操作系统最核心的概念 ...

  8. Python之路【第九篇】:Python操作 RabbitMQ、Redis、Memcache、SQLAlchemy

    Python之路[第九篇]:Python操作 RabbitMQ.Redis.Memcache.SQLAlchemy   Memcached Memcached 是一个高性能的分布式内存对象缓存系统,用 ...

  9. Python开发【第二十一篇】:Web框架之Django【基础】

    Python开发[第二十一篇]:Web框架之Django[基础]   猛击这里:http://www.cnblogs.com/wupeiqi/articles/5237704.html Python之 ...

随机推荐

  1. CSS系列:CSS表格样式

    1. 设置单元格的边框 border-collapse: collapse; 2. 边框的分离 对table使用CSS实现cellspacing的属性border-spacing. border-sp ...

  2. wpa supplicant 保存 wifi 设置

    wpa suppliclant使用wpa gui连接wifi后,下次开机的时,不能保存,需要从新手动进行连接. 自动保存方法: 配置文件/etc/wpa_supplicant.conf 添加 upda ...

  3. HDU 1695 GCD (欧拉函数&plus;容斥原理&rpar;

    GCD Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submiss ...

  4. ios7开发学习笔记-包括c oc 和ios介绍

    请查看我的新浪资料分享 http://iask.sina.com.cn/u/2430843520

  5. CSS中应用position的absolute和relative的属性制作浮动层

    我的浮动层结构大概如下: <div id="father"> <div id="son"> </div> </div& ...

  6. SqlAlchemy初探

    SqlAlchemy是Python下的一个成熟的ORM框架.下面我们对他的使用做一个简略的介绍. 0.安装 如果有pip,使用pip安装,更便捷.pip install sqlalchemy 也可以下 ...

  7. TypeScript学习笔记(三):类

    类 在TypeScript中,类似于C#的结构,即一个文件中可以存在多个类,且文件名可以任意取,我们先看一个简单的类的示例. class Person { private name: string; ...

  8. 一句代码,更加优雅的调用KVO和通知

    来源:wazrx 链接:http://www.jianshu.com/p/70b2503d5fd1 写在前面 每次使用KVO和通知我就觉得是一件麻烦的事情,即便谈不上麻烦,也可说是不方便吧,对于KVO ...

  9. Docker进阶之一&colon;Docker介绍与体系结构

    一.  Docker是什么 Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从Apache2.0协议开源,基于Linux内核的cgroup,namespace,Union FS 等技术, ...

  10. c&num;基础系列2---深入理解 String

    "大菜":源于自己刚踏入猿途混沌时起,自我感觉不是一般的菜,因而得名"大菜",于自身共勉. 扩展阅读:深入理解值类型和引用类型 基本概念 string(严格来说 ...