Python基础之面向对象进阶

Python基础之面向对象进阶

  • 一:面向对象之组合
  • 二:单例设计模式
    • 2.1 __ new__方法
  • 三:抽象类
    • 3.1 继承的两种用途
    • 3.2 什么是抽象类
    • 3.3 什么是接口
    • 3.4 抽象类与接口
    • 3.5 接口与归一化设计
  • 四:反射
    • 4.1反射类中的变量
    • 4.2 反射对象中的变量

一:面向对象之组合

组合即在类中封装另一个类的对象。
组合的意义:让类的对象与另一个类的对象产生关系,类与类之间产生关系。
让两个类之间产生关系:一是继承,二是组合。

class GameRole:
    def __init__(self, nickname, ad, hp):
        self.nickname = nickname
        self.ad = ad  # 血值
        self.hp = hp  # 生命值
    def attack(self, role):
        role.hp = role.hp - self.ad
        print('%s攻击%s,%s掉了%s血,还剩%s血' % (self.nickname, role.nickname, role.nickname, self.ad, role.hp))
    def equip_weapon(self, w):
    	# 封装武器类对象
        self.weapon = w

class Weapon:
    def __init__(self, name, ad):
        self.name = name
        self.ad = ad
    def fight(self, role1, role2):
        role2.hp = role2.hp - self.ad
        print('%s 用 %s 攻击了 %s,%s掉了%s血,还剩%s血' % (
        role1.nickname, self.name, role2.nickname, role2.nickname, self.ad, role2.hp))

p1 = GameRole('Cameron', 20, 500)
p2 = GameRole('willis', 100, 200)
# p1.attack(p2)
# print(p2.hp)
w1 = Weapon('屠龙刀', 30)
w2 = Weapon('倚天剑', 35)
# w1.fight(p1, p2)  # 这样不合理,攻击的发起者应该是人
p1.equip_weapon(w1)
p2.equip_weapon(w2)
print(w1)
print(p1.weapon)
p1.weapon.fight(p1,p2)

二:单例设计模式

目的:让类创建的对象,在系统中只有唯一的一个实例,每一次执行返回的对象,内存地址是相同的。

2.1 __ new__方法

使用类名()创建对象时,Python解释器首先会调用 __new __方法为对象分配空间
__ new__是一个由object基类提供的内置的静态方法,主要作用有两个:

  1. 在内存中为对象分配空间
  2. 返回对象的引用

Python解释器获得对象的引用后,将引用作为第一个参数传递给__ init__方法

重写__ new__ 方法代码非常固定:

  1. 重写__ new__方法一定要 return super().__ new__(cls),否则Python解释器得不到分配了空间的对象引用,就不会调用对象的初始化方法
  2. 注意:__ new__是一个静态方法,在调用时需要主动传递 cls 参数
class MusicPlayer(object):
    instance = None
    def __new__(cls, *args, **kwargs):
        if cls.instance is None:
            cls.instance = super().__new__(cls)
        return cls.instance

    def __init__(self):
        print('播放器初始化')

p1 = MusicPlayer()
print(p1)
p2 = MusicPlayer()
print(p2)

输出结果:

播放器初始化
<__main__.MusicPlayer object at 0x000001E3E04DB648>
播放器初始化
<__main__.MusicPlayer object at 0x000001E3E04DB648>

从以上输出结果可以出,内存地址都是一样的。但是初始化方法被调用了两次,那么有没有办法让初始化方法也只执行一次呢?

class MusicPlayer(object):
    instance = None  # 记录第一个被创建对象的引用
    init_flag = False  # 记录是否执行过初始化动作

    def __new__(cls, *args, **kwargs):
        if cls.instance is None:
            cls.instance = super().__new__(cls)
        return cls.instance

    def __init__(self):
        if MusicPlayer.init_flag:
            return
        print('播放器初始化')
        MusicPlayer.init_flag = True

p1 = MusicPlayer()
print(p1)
p2 = MusicPlayer()
print(p2)

输出结果:

播放器初始化
<__main__.MusicPlayer object at 0x0000026292E34588>
<__main__.MusicPlayer object at 0x0000026292E34588>

三:抽象类

3.1 继承的两种用途

1.继承基类的方法,并且做出自己的改变或者扩展(代码复用)
2.声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现

接口的功能,子类继承接口类,并且实现接口中的功能。

3.2 什么是抽象类

与java一样,python也有抽象类的概念,但是需要借助模块实现。抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化。
抽象类与普通类的不同之处在于:抽象类中只能有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法。

3.3 什么是接口

接口可以理解为自己给使用者来调用自己功能方法的入口。

为什么要用接口:
(1)可以实现权限控制,比如可以通过接口做一下访问控制,可以允许或者拒绝调用者的一些操作。
(2)降低了使用者的使用难度,使用者只需要知道怎么调用即可,不需要知道里边的具体实现方法。

3.4 抽象类与接口

抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性。
抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计

3.5 接口与归一化设计

归一化让使用者无需关心对象的类是什么,只需要知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。
归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合。

import abc
class Payment(metaclass=abc.ABCMeta):
	# 抽象类不能被实例化
    # 抽象类(接口类):强制制定一个规范,凡是继承该类的类中必须要有pay方法,如果没有,实例化对象时会抛异常
    @abc.abstractmethod		# python3.3以后使用该装饰器
    def pay(self): pass
class Alipay(Payment):
    def __init__(self, money):
        self.money = money
    def pay(self):
        print('使用支付宝支付了%s' % self.money)
class Jdpay(Payment):
    def __init__(self, money):
        self.money = money
    def pay(self):
        print('使用京东支付了%s' % self.money)
class Wechatpay(Payment):
    def __init__(self, money):
        self.money = money
    # 继承了Payment类就必须实例pay方法
def pay(obj):
    obj.pay()
a = Alipay(2000)
j = Jdpay(4000)
w = Wechatpay(4320)
pay(a)

Wechatpay类继承了Payment类,但并未实现pay方法,会抛出异常,如下:

TypeError: Can't instantiate abstract class Wechatpay with abstract methods pay

四:反射

反射:使用字符串数据类型的变量名来获取这个变量的值

4.1反射类中的变量

# getattr(变量名:命名空间,字符串:属于一个命名空间内的变量名)
class Foo:
    school = 'oldboy'
    country = 'China'
    language = 'Chinese'
    @classmethod
    def class_method(cls):
        print(cls.school)
    @staticmethod
    def static_method():
        print('in staticmethod')
while True:
    choice = input('>>> ')
    if hasattr(Foo, choice):
        print(getattr(Foo, choice))		# 判断类中是否有该属性
        if choice == 'class_method':
            getattr(Foo, 'class_method')()

4.2 反射对象中的变量

class Foo:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def eating(self):
        print('%s is eating' % self.name)
poe = Foo('poe', 34)
print(getattr(poe, 'name'))
print(getattr(poe, 'age'))
getattr(poe, 'eating')()

你可能感兴趣的:(python)