python中的类与继承

1. 为什么要使用面向对象?

初学时认为函数就可以满足功能的实现,没必要使用面向对象,随着日常需求的增长和代码量的增多,发现面向对象确实太有必要了。否则代码管理很费劲,变更容易出错,改了这个那个没改,总是在做一些让人头疼的事。面向对象就会很好解决这个问题,在代码管理和变更改进或者扩充上都省了不少力,也不用重新debug。

2. 面向对象的基本形式

class MyBaseClass(object):
    def __init__(self, value):
        self.value = value

    def getvalue(self):
        return self.value

    def setvalue(self, value):
        self.value = value

a = MyBaseClass(10)
print(a.getvalue())
a.setvalue(15)
print(a.getvalue())

 

 返回值:

10
15

 以上是一个最基本的类。每个类中都有__init__(),是类的构造方法,在对象实例化后即被调用,
a = MyBaseClass(10)对象实例化后即调用了__init__方法。因为这个问题会导致多重继承时,会导致公共基类执行多次,需要使用内置super函数来初始化父类。

class MyBaseClass(object):
    def __init__(self, value):
        self.value = value

class TimesTwo(object):
    def __init__(self):
        self.value *= 2

class PlusFive(object):
    def __init__(self):
        self.value += 5

class OneWay(MyBaseClass, TimesTwo, PlusFive):
    def __init__(self, value):
        MyBaseClass.__init__(self, value) # value =5
        TimesTwo.__init__(self) #value = 10
        PlusFive.__init__(self)# value = 15

foo = OneWay(5)
print(foo.value)

结果与继承超类顺序的__init__顺序相同。

 

3. 用super 初始化父类,解决由__init__导致的问题

super() 函数是用于调用父类(超类)的一个方法。

super() 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。

MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。

 

钻石继承: 子类继承自两个单独的超类,而两个超类又继承自同一个公共基类。

class MyBaseClass(object):
    def __init__(self, value):
        self.value = value

class TimesTwo(MyBaseClass):
    def __init__(self, value):
        MyBaseClass.__init__(self,value)
        self.value *= 2

class PlusFive(MyBaseClass):
    def __init__(self,value):
        MyBaseClass.__init__(self,value)
        self.value += 5

class ThisWay(PlusFive,TimesTwo):
    def __init__(self, value):
        PlusFive.__init__(self, value)# value = 13
        TimesTwo.__init__(self, value) #value = 16

foo = ThisWay(8)
print(foo.value)

输出结果是16。因为执行顺序为PlusFive的构造函数后调用基类MyBaseClass的构造函数,value=8,PLusFive构造函数value=13,执行TimeTwo的构造函数会再次调用基类MyBaseClass的构造函数value=8,再TimeTwo构造函数value=16。

显然我们期待的是 (8+5)*2 ,原因就是构造方法中的__init__无法继承, 需要在构造函数中绑定父类中的__init__,这会导致基类的构造函数运行多次导致,为了解决这个问题,用super初始化超类,避免这种问题产生。

class MyBaseClass(object):
    def __init__(self, value):

        self.value = value

class TimesTwo(MyBaseClass):
    def __init__(self, value):
        super(TimesTwo, self).__init__(value)
        self.value *= 2

class PlusFive(MyBaseClass):
    def __init__(self,value):
        super(PlusFive, self).__init__(value)
        self.value += 5

class ThisWay(TimesTwo, PlusFive):
    def __init__(self, value):
        super(ThisWay, self).__init__(value)

foo = ThisWay(8)
print(foo.value)

from pprint import pprint
pprint(ThisWay.mro())
26
[<class '__main__.ThisWay'>,
 <class '__main__.TimesTwo'>,
 <class '__main__.PlusFive'>,
 <class '__main__.MyBaseClass'>,
 <class 'object'>]
class ThisWay(PlusFive, TimesTwo):
    def __init__(self, value):
        super(ThisWay, self).__init__(value)


21
[<class '__main__.ThisWay'>,
 <class '__main__.PlusFive'>,
 <class '__main__.TimesTwo'>,
 <class '__main__.MyBaseClass'>,
 <class 'object'>]

结果和ThisWay定义超类顺序有关。

第一种情况先定义TimesTwo,后定义PlusFive,根据MRO可以看到初始化顺序,倒过来即是执行顺序,(8+5)*2=26。

第二种情况同理,8*2 +5 = 21

以上对super是Python2 的定义形式,比较复杂。需要指定当前类和指定相关的方法名称,这里是__init__(),以及方法的参数。

下面是Python3 定义形式:

class MyBaseClass(object):
    def __init__(self, value):
        self.value = value

class TimesTwo(MyBaseClass):
    def __init__(self, value):
        super().__init__(value)
        self.value *= 2

class PlusFive(MyBaseClass):
    def __init__(self,value):
        super().__init__(value)
        self.value += 5

class ThisWay(PlusFive, TimesTwo):
    def __init__(self, value):
        super().__init__(value)

foo = ThisWay(8)
print(foo.value)

 代码比较简洁。

4. @classmethod 类的多态

继承体系中的多个类都能以各自所独有的方式来实现某个方法。

通过@classmethod,可以用一种与构造器类似的方式来构造类的对象。

class Data_test(object):
    day=0
    month=0
    year=0
    def __init__(self,year=0,month=0,day=0):
        self.day=day
        self.month=month
        self.year=year
 
    def out_date(self):
        print "year :"
        print self.year
        print "month :"
        print self.month
        print "day :"
        print self.day
 
t=Data_test(2016,8,1)
t.out_date()
 
  

 如果用户输入的是 "2016-8-1" 这样的字符格式,那么就需要调用Date_test 类前做一下处理:
 

string_date='2016-8-1'
year,month,day=map(int,string_date.split('-'))
s=Data_test(year,month,day)
 


 先把‘2016-8-1’ 分解成 year,month,day 三个变量,然后转成int,再调用Date_test(year,month,day)函数。 也很符合期望。
 
那我可不可以把这个字符串处理的函数放到 Date_test 类当中呢?
 
那么@classmethod 就开始出场了

class Data_test2(object):
    day=0
    month=0
    year=0
    def __init__(self,year=0,month=0,day=0):
        self.day=day
        self.month=month
        self.year=year
 
    @classmethod
    def get_date(cls,string_date):
        #这里第一个参数是cls, 表示调用当前的类名
        year,month,day=map(int,string_date.split('-'))
        date1=cls(year,month,day)
        #返回的是一个初始化后的类
        return date1
 
    def out_date(self):
        print "year :"
        print self.year
        print "month :"
        print self.month
        print "day :"
        print self.day
 

 在Date_test类里面创建一个成员函数, 前面用了@classmethod装饰。 它的作用就是有点像静态类,比静态类不一样的就是它可以传进来一个当前类作为第一个参数。
那么如何调用呢?

r=Data_test2.get_date("2016-8-6")
r.out_date()
输出:
year :
2016
month :
8
day :
1

这样子等于先调用get_date()对字符串进行出来,然后才使用Data_test的构造函数初始化。
这样的好处就是你以后重构类的时候不必要修改构造函数,只需要额外添加你要处理的函数,然后使用装饰符 @classmethod 就可以了。

https://www.cnblogs.com/baxianhua/p/10845620.html

关于@classmethod 还是没有看懂。

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值