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 还是没有看懂。