面向对象里的类和实际中的类是不完全一样的
一. 静态属性、静态方法、类方法
静态属性:将类中的函数封装,实现像调用数据属性一样的方式调用函数属性
封装:使得调用者无法察觉到被调用对象的实现逻辑,就是对象的封装
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中类的继承分为单继承和多继承
子类继承了父类的所有属性,子类自定义的属性如果跟父类重名了,那就覆盖了父类的属性-----》这样的说法是错误的,其实质是作用域问题
当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好
当类之间有很多相同的功能,提取这些共同的功能做成积累,用继承比较好
继承同时具有两种含义
- 继承基类的方法,并且做出自己的改变或者拓展(代码重用)
- 声明某个子类兼容于某基类,定义一个接口类,子类继承接口类,并且实现接口中定义的方法
实践中,第一种继承使得子类与基类强耦合,是非常糟糕的,而第二种含义则非常重要,又称为“接口继承‘。接口继承实质上是要求”做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的对象“——这在程序设计上,叫做归一化
比如:我们有一个汽车接口,里面定义了汽车所有的功能,然后由本田汽车的类,奥迪汽车的类,大众汽车的类,他们都实现了汽车接口,这样就好办了,大家只需要学会了怎么开汽车,那么无论是本田,还是奥迪,还是大众我们都会开了,开的时候根本无需关心我开的是哪一类车,操作手法(函数调用)都一样。
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. 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的设计。