面向对象之组合、多态及反射

面向对象之组合、多态及反射

一 组合

1 什么是组合

​ 在一个类中以另外一个类的对象作为数据属性,称之为类的组合。即把另一个类的对象赋值给当前对象的属性。

2 为何要用组合

​ 组合与继承都是用来解决代码的重要性问题。

​ 不同的是,继承是一种“是”的关系,当类之间有很多相同之处,应该使用继承;

​ 而组合是一种“有”的关系,并且较小的类是较大的类的组件时,应该使用组合。

3 如何使用组合

#####——————————#####  把另一个类的对象赋值给当前对象的属性
class Teacher:
    def __init__(self, name, age, gender, level):
        self.name = name
        self.age = age
        self.gender = gender
        self.level = level

    def tell(self):
        print('%s:%s' % (self.name, self.age))


class Student:
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender


class Course:
    def __init__(self, name, price, period):
        self.name = name
        self.price = price
        self.period = period

    def tell(self):
        print('<%s:%s:%s:>' % (self.name, self.price, self.period))


tea1 = Teacher('矮根', 88, 'male', 10)
stu1 = Student('alex', 99, 'male')

python = Course('python开发', 30000, '6mons')
linux = Course('linux课程', 30000, '6mons')

tea1.course = [python, linux]  # 把课程对象赋值给了老师对象
stu1.course = python           # 把课程对象赋值给学生对象

stu1.course.tell()             # 学生对象可以使用课程对象的属性
'''<python开发:30000:6mons:>'''
for course_obj in tea1.course:
    course_obj.tell()          # 老师对象可以使用课程对象的属性
"""
<python开发:30000:6mons:>
<linux课程:30000:6mons:>
"""

二 多态

1 什么是多态

​ 多态指的是同一类事物有多种形态,比如动物有多种形态:猫、狗、猪等等。

import abc
class Animal(metaclass=abc.ABCMeta): # 同一类事物:动物
    @abc.abstractmethod				# 使用了abc模块之后,就人为地限制了
    							  # 被abstractmethod装饰的方法talk,在子类中必须有!
    def talk(self):
        pass
class Cat(Animal): #动物的形态之一:猫
    def talk(self):
        print('喵喵喵')
class Dog(Animal): #动物的形态之二:狗
    def talk(self):
        print('汪汪汪')
class Pig(Animal): #动物的形态之三:猪
    def talk(self):
        print('哼哼哼')

#实例化得到三个对象
>>> cat=Cat()
>>> dog=Dog()
>>> pig=Pig()

2 为何要有多态

​ 为了我们可以在不考虑某一个对象的具体属性的前提下,直接使用该对象。

3 如何使用多态

3.1 基本使用,直接调用方法:

​ 需要我们在设计程序时,把对象的使用方法进行统一,让我们可以在不考虑对象的具体情况下,可以直接使用他们的方法。

>>> cat.talk()
喵喵喵
>>> dog.talk()
汪汪汪
>>> pig.talk()
哼哼哼

3.2 定义接口使用

​ 甚至更进一步,我们可以定义一个统一的接口来使用:

>>> def Talk(animal):
...     animal.talk()
... 
>>> Talk(cat)
喵喵喵
>>> Talk(dog)
汪汪汪
>>> Talk(pig)
哼哼哼

3.3 多态的本质

​ 多态性的本质在于不同的类中定义有相同的方法名,这样我们就不用考虑类而统一用一种方式去使用对象。甚至我们可以通过在父类中引入抽象类的概念来硬性限制子类必须有某些方法名,但是这样会提高程序的耦合性,所以一般不使用。

4 鸭子特性:duck typing

​ 我们完全可以不依赖继承,只需要制造出外观和行为相同的对象,同样可以实现不考虑对象类型而使用对象,这正是Python崇尚的鸭子类型(“如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子”) 。比起继承的方式,鸭子类型具有松耦合度。

​ 使用了abc模块之后,就人为地限制了子类的方法,不符合鸭子类型的思想。

​ go语言也是崇尚鸭子类型,go使用接口定义行为,可以选择遵循该接口,实现接口的所有方法,就是鸭子类型。

# 二者看起来都像文件,因而就可以当文件一样去用,然而它们并没有直接的关系
class Txt: 			# Txt类有两个与文件类型同名的方法,即read和write
    def read(self):
        pass
    def write(self):
        pass

class Disk: 		# Disk类也有两个与文件类型同名的方法:read和write
    def read(self):
        pass
    def write(self):
        pass

5 一切皆为对象

​ 在Python中,一切皆为对象,本身就支持多态性。

​ 所有的数据类型,都是对象。

# 所有的数据类型,都是对象,数据类型都在背后做了一个:类型()的操作
x = 11  # x=int(11)
print(int)  # <class 'int'>

class Foo:
    pass
print(Foo)  # <class '__main__.Foo'>

x = [1, 2, 3]  # 调用:list([1,2,3])
y = [111, 222]  # 调用:list([1,2,3])
x.append(4)  # 前面是简写————》实际上背后发生的事情:list.append(x, 4)
y.append(3333)  # 前面是简写————》实际上背后发生的事情:list.append(y, 3333)

print(type(x))  # <class 'list'>
# 我们可以在不考虑三者类型的情况下直接使用统计三个对象的长度
s.__len__()
l.__len__()
t.__len__()

# Python内置了一个统一的接口
len(s)
len(l)
len(t)

三 反射

1 什么是反射

​ 反射是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省) 。

2 为何要使用反射

​ 可以反射当前模块成员,导入其他模块,利用反射查找该模块是否存在某个方法。否则你需要使用dir( )方法才能知道有哪些方法。

3 python面向对象中的反射:

​ 通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射) 。

​ 四个可以实现自省的函数,适用于类和对象(一切皆对象,类本身也是一个对象) 。

3.1 hasattr(object, name)

​ 判断object中有没有一个name字符串对应的方法或属性。

class Foo:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def f1(self):
        print('from f1')


obj = Foo(111, 222)

print(hasattr(obj, 'x'))  # True

3.2 getattr(object, name, default=None)

​ 获取属性

class Foo:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def f1(self):
        print('from f1')


obj = Foo(111, 222)

print(getattr(obj, 'xxx', '没有这个值'))  # 没有这个值

3.3 setattr(x, y, v)

​ 设置属性

class Foo:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def f1(self):
        print('from f1')


obj = Foo(111, 222)

setattr(obj, 'xxx', 1111)  # obj.xxx = 1111
print(obj.xxx)  # 1111

3.4 delattr(x, y)

​ 删除属性

class Foo:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def f1(self):
        print('from f1')


obj = Foo(111, 222)

delattr(obj, 'xxx')  # def obj.xxx
print(obj.xxx)  # AttributeError: 'Foo' object has no attribute 'xxx'

四 内置方法

​ 内置方法都是在满足某种条件下触发的。

1 __str__ 改变对象的字符串显示

#####——————————#####  不使用__str__方法,无法直接打印出对象的信息
class People:
    def __init__(self, name, age):
        self.name = name
        self.age = age

obj = People("egon", 18)

print(obj)  # <__main__.People object at 0x0000022C3D135A30>

#####——————————#####  使用了__str__方法,可以直接输出对象的属性信息
class People:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        # print('===>')
        return "<%s:%s>" %(self.name,self.age)

obj = People("egon", 18)

print(obj)  # print(obj.__str__())

2 __del__析构方法,当对象在内存中被释放时,自动触发执行

​ 如果产生的对象仅仅只是python程序级别的(用户级) ,那么无需定义__del __ ,如果产生的对象的同时还会向操作系统发起系统调用,即一个对象有用户级与内核级两种资源,比如(打开一个文件,创建一个数据库链接) ,则必须在清除对象的同时回收系统资源,这就用到了 __del __

#####——————————#####  
class People:
    def __init__(self, name, age,f):
        self.name = name
        self.age = age
        self.f = f

    def __del__(self):
        print('===>')
        # 回收资源
        self.f.close()

obj = People("egon", 18,open("a.txt",'w',encoding='utf-8'))

del obj
print('运行完毕...')
posted @ 2020-03-26 08:10  越关山  阅读(94)  评论(0)    收藏  举报