Day25 静态、组合、继承

本文介绍了Python中面向对象编程的核心概念,包括类的静态属性、类方法和静态方法的差异,以及组合、继承的使用。静态属性允许函数作为属性调用,类方法不依赖实例,静态方法不与类或实例绑定。文章还探讨了组合在解决代码冗余问题上的应用,以及继承的两种含义:代码重用和接口继承。此外,提到了Python中的多重继承和MixIn设计模式。
摘要由CSDN通过智能技术生成

面向对象里的类和实际中的类是不完全一样的

一. 静态属性、静态方法、类方法

静态属性:将类中的函数封装,实现像调用数据属性一样的方式调用函数属性

封装:使得调用者无法察觉到被调用对象的实现逻辑,就是对象的封装

class Room:
    def __init__(self, name, owner, width, length, height):
        self.name = name
        self.owner = owner
        self.width = width
        self.length = length
        self.height = height

    @property
    def cal_area(self):
        return self.width * self.length	   # 注意为了实现和数据属性相同的调用方式,需要加入return值

R1 = Room("公寓", "MB", 100, 100, 100)
print(R1.name)
print(R1.cal_area)    # 这里函数属性的调用已经完全和数据属性相同了
# 让用户使用时无法随意修改属性

类方法:当希望类中的函数不和实例捆绑时,即类可以在不声明实例的情况下调用类中的函数

一般在类中定义函数时都会带有self

class Room:
    tag = 1
    def __init__(self, name, owner, width, length, height):
        self.name = name
        self.owner = owner
        self.width = width
        self.length = length
        self.height = height

    @classmethod
    def tell_info(cls):
        print(cls)
        print(cls.tag)	#等价于Room.tag

Room.tell_info()

# 输出
<class '__main__.Room'>
1

静态方法:只是名义上归属类管理,通过类和实例都可以调用,即这个属性既不与类绑定也不与实例绑定,只是类的工具包

class Room:
    tag = 1
    def __init__(self, name, owner, width, length, height):
        self.name = name
        self.owner = owner
        self.width = width
        self.length = length
        self.height = height

    @staticmethod
    def wash_body(a,b,c):
        print(a,b,c)

Room.wash_body(a,b,c)
r1 = Room("豪斯", "dd", 10, 10, 10)
r1.wash_body(a,b,c)

总结

静态属性:即可以访问实例属性,也可以访问类属性

类方法:可以访问类属性,不能访问实例属性

静态方法:不能访问类属性也不能访问实例属性

二. 组合

一个对象的属性值是另一个类的实例对象,用来解决类和类之间代码冗余问题

class Person:
    school = 'oldboy'

    
class Teacher(Person):
    def __init__(self,name,age,level,course):
        self.name=name
        self.age=age
        self.level=level
        #course是课程对象,表示老师教授的课程
        self.course=course

class Student(Person):
    def __init__(self,name,age,course):
        self.name=name
        self.age=age
        # course是课程对象,表示学生选的课程
        self.course = course

class Course:
    # 把这个course类实例化过后,在实例化老师或者学生的时候,当作属性的值传递给老师或学生的course属性值
    def __init__(self,course_name,course_price,course_period):
        self.name=course_name
        self.price=course_price
        self.period=course_period

course=Course('Python',20180,7) # 实例化课程对象
stu=Student('nick',19,course) # 实例化学生对象:course是课程对象
teacher=Teacher('nick',19,'高级',course) # 实例化老师对象:course是课程对象
# 查看老师教授的课程名
print(teacher.course.name)

三. 继承

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

类的继承,父类又称为基类。python中类的继承分为单继承和多继承

子类继承了父类的所有属性,子类自定义的属性如果跟父类重名了,那就覆盖了父类的属性-----》这样的说法是错误的,其实质是作用域问题

当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好

当类之间有很多相同的功能,提取这些共同的功能做成积累,用继承比较好

继承同时具有两种含义

  1. 继承基类的方法,并且做出自己的改变或者拓展(代码重用)
  2. 声明某个子类兼容于某基类,定义一个接口类,子类继承接口类,并且实现接口中定义的方法

实践中,第一种继承使得子类与基类强耦合,是非常糟糕的,而第二种含义则非常重要,又称为“接口继承‘。接口继承实质上是要求”做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的对象“——这在程序设计上,叫做归一化

比如:我们有一个汽车接口,里面定义了汽车所有的功能,然后由本田汽车的类,奥迪汽车的类,大众汽车的类,他们都实现了汽车接口,这样就好办了,大家只需要学会了怎么开汽车,那么无论是本田,还是奥迪,还是大众我们都会开了,开的时候根本无需关心我开的是哪一类车,操作手法(函数调用)都一样。

import abc
#抽象类加抽象方法就等于面向对象编程中的接口

class File(metaclass=abc.ABCMeta):	#指定这是一个抽象类
    @abc.abstractmethod		#抽象方法
    def read(self): #定接口函数read
        pass
 
    @abc.abstractmethod
    def write(self): #定义接口函数write
        pass

    def necc(self):	#可以不用实现
 pass
 
class Process(File):		#必须实现File类中的抽象方法@abc.abstractmethod
    def read(self):
        print('进程数据的读取方法')
        # pass
    def write(self):
        print('进程数据的读取方法')
 
p=Process()
p.read()

调用顺序

对于定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表。该列表那个过一个C3线性算法来实现。

  1. 子类会先于父类被检查
  2. 多个父类会根据它们再列表中的顺序被检查
  3. 如果对下一个类存在两个合法的选择,选择第一个父类

子类中调用父类的方法

子类继承了父类的方法,然后想进行修改(在原有基础上进行需改),那么就需要再子类中调用父类的方法

1. 父类名.父类方法

当父类名出现修改时,需要把该形式的代码都要修改,扩展性不强

2. super().父类方法

判断对象类型

1. 使用type()函数,无论是基本类型亦或是指向函数或者类的变量,都可以使用type()判断

type()返回对应Class类型

type of 123: <class 'int'>

type of func: <class 'builtin_function_or_method'>

判断一个对象是否是函数,使用types模块中定义的常量

>>> import types
>>> def fn():
...     pass
...
>>> type(fn)==types.FunctionType
True
>>> type(abs)==types.BuiltinFunctionType
True
>>> type(lambda x: x)==types.LambdaType
True
>>> type((x for x in range(10)))==types.GeneratorType
True

2. 要判断class类型,则使用isinstance(),可以判断一个对象是否是该类型本身,或者位于该类型的父继承链上

isinstance()还可以判断一个变量是否是某些类型的一种

>>> isinstance([1, 2, 3], (list, tuple))
True
>>> isinstance((1, 2, 3), (list, tuple))
True

如果要获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list

print(dir(123))

# 输出
['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']

类似__xxx__的属性和方法在Python中都是有特殊用途的,比如__len__方法返回长度。在Python中,如果你调用len()函数试图获取一个对象的长度,实际上,在len()函数内部,它自动去调用该对象的__len__()方法,所以,下面的代码是等价的:

>>> len('ABC')
3
>>> 'ABC'.__len__()
3

多重继承

在设计类的继承关系时,通常,主线都是单一继承下来的,但是,如果需要“混入”额外的功能,通过多重继承就可以实现。这种设计通常称之为MixIn

MixIn的目的就是给一个类增加多个功能,这样,在设计类的时候,我们优先考虑通过多重继承来组合多个MixIn的功能,而不是设计多层次的复杂的继承关系。

由于Python允许使用多重继承,因此,MixIn就是一种常见的设计。

只允许单一继承的语言(如Java)不能使用MixIn的设计。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值