组合即在类中封装另一个类的对象。
组合的意义:让类的对象与另一个类的对象产生关系,类与类之间产生关系。
让两个类之间产生关系:一是继承,二是组合。
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)
目的:让类创建的对象,在系统中只有唯一的一个实例,每一次执行返回的对象,内存地址是相同的。
使用类名()创建对象时,Python解释器首先会调用 __new __方法为对象分配空间
__ new__是一个由object基类提供的内置的静态方法,主要作用有两个:
Python解释器获得对象的引用后,将引用作为第一个参数传递给__ init__方法
重写__ new__ 方法代码非常固定:
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>
1.继承基类的方法,并且做出自己的改变或者扩展(代码复用)
2.声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现
接口的功能,子类继承接口类,并且实现接口中的功能。
与java一样,python也有抽象类的概念,但是需要借助模块实现。抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化。
抽象类与普通类的不同之处在于:抽象类中只能有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法。
接口可以理解为自己给使用者来调用自己功能方法的入口。
为什么要用接口:
(1)可以实现权限控制,比如可以通过接口做一下访问控制,可以允许或者拒绝调用者的一些操作。
(2)降低了使用者的使用难度,使用者只需要知道怎么调用即可,不需要知道里边的具体实现方法。
抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性。
抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计
归一化让使用者无需关心对象的类是什么,只需要知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。
归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合。
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
反射:使用字符串数据类型的变量名来获取这个变量的值
# 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')()
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')()