1. 类中方法
在python中,类中方法包括多种,其中有:“魔法”方法、普通(公有)方法、私有方法、类方法、静态方法。
“魔法”方法: 形式是:__***__(),如:__init__(self)、__str__(self, *args, **kwargs)等自带的方法,该方法只能通过对象调用,如果不重写,则调用时默认使用自带的实现,例如:
class Test():
#构造方法
def __init__(self): #1
print("test_init")
if __name__ == "__main__":
print(Test().__str__()) #2
输出:
<__main__.Test object at 0x0000015D7D7B30B8>
第一段代码
调用魔法方法__str__输出是一串地址,其实__str__方法实现默认的是返回str(self),因此print(Test().__str__())和print(str(Test()))返回结果是一样的,如下所示。
class Test():
#构造方法
def __init__(self): #1
return
if __name__ == "__main__":
print(Test().__str__()) #2
print(str(Test()))
输出:
<__main__.Test object at 0x0000023FBAA23080>
<__main__.Test object at 0x0000023FBAA23080>
第二段代码
需要注意的一个概念是,在上述代码中#1的__init__(self)为类的构造方法,而self表示的是对象本身,就如同java中隐式的this,该形参必须存在(当然名字可以不需要使用self, 并非强制要写为self),可以这么理解:把self当成方法的形参,但是该形参对应的实参必须是对象名,如果不显性对该形参传入实参,则默认传入的实参是引用方法的对象;如果显性对该形参传入实参,则需要传入的实参是对象名,但是引用方法时采用的是类名进行引用,以魔法方法__str__为例,其原型是__str__(self),举例如下所示。
class Test():
#构造方法
def __init__(self):
return
if __name__ == "__main__":
#显性声明一个对象
test = Test()
#类名Test引用方法,但是将对象名test作为实参传入
print(Test.__str__(test))
#对象名引用方法,不需要显性传实参,默认引用对象传入
print(test.__str__())
第三段代码
需要注意的另外一个概念是,在上述第一段和第二段代码中#2号注释,采用的是隐形声明对象(可理解为匿名对象),其与第三段代码中显性声明test对象,然后在引用魔法__str__()不同的是,第一段代码和第二段代码中省略了显性声明对象的步骤,直接采用:类名().方法 的形式引用方法。
魔法方法如果被重写,输出的将是重写后的结果,以__str__()为例,如下所示(构造方法__init__也被重写了,如果不重写将输出None)。
class Test():
#构造方法
def __init__(self):
return
def __str__(self):
return "test_str"
if __name__ == "__main__":
print(Test().__str__())
输出:
test_str
是否好奇这些魔法方法是哪里来的?明明在新建Test类时,没有看到魔法方法,其实在python 3.x中,每个类在创建时,默认继承了object类,显性继承object类与非显性继承都是一样的,但是在python 2.x时,显性和非显性继承object类还是有差异的,其默认的魔法方法有区别,而python 3.x没有差异了,如下所示。
python 3.x
显性继承object类:
class Test(object):
def testPrint(self):
print("test")
if __name__ == "__main__":
print(dir(Test))
输出:
#其中testPrint属于普通实现方法,其他的都是继承object得到的魔法方法。
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'testPrint']
非显性继承object类:
class Test:
def testPrint(self):
print("test")
if __name__ == "__main__":
print(dir(Test))
输出:
#输出结果与显性继承object类一样
python 2.x
显性继承object类
class Test(object):
def __init__(self):
print("test_init")
print dir(Test)
输出:
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
非显性继承object类
class Test:
def __init__(self):
print("test_init")
print dir(Test)
输出:
['__doc__', '__init__', '__module__']
普通(公有)方法:形式为def publicMethod(self, ***),至少有一个self,所以方法只能由对象调用,如果采用类名调用,需要显性通过以对象名作为实参传入,例如:
class Test():
#构造方法
def __init__(self):
return
def __str__(self):
return "test_str"
#公有方法,由对象调用,如果类名调用,需要通过显性传入对象名作为实参
def publicMethod(this):
return "publicMethod"
if __name__ == "__main__":
test = Test()
print(test.publicMethod())
print(Test.publicMethod(test))
输出:
publicMethod
publicMethod
私有方法:形式为def __privateMethod(self, ***),在公有函数名前加入两条下划线__,需要区别“魔法”方法,在私有函数名后并没有两条下划线,该方法只能在类的内部能被调用,在类外是不可见的。如果类被继承,其私有方法不可被继承。如果需要使用私有方法,则可通过公有方法进行封装,对外部进行可见,例如:
class Test():
#构造方法
def __init__(self):
return
def __str__(self):
return "test_str"
#公有方法,由对象调用,如果类名调用,需要通过显性传入对象名作为实参
def publicMethod(this):
return "publicMethod"
#私有方法,只能在类中调用
def __privateMethod(self):
return "privateMethod"
def getPrivateMethodContext(self):
return self.__privateMethod()
if __name__ == "__main__":
test = Test()
print(test.getPrivateMethodContext())
输出:
privateMethod
类方法:形式为def classMethod(cls),该方法直接通过类名进行调用(需要在函数名上加一个@classmethod方可被类名调用,因为在上文中提起类中方法self可以使用其他命名,那么也就是说并非self不可,所以如果self改成cls是否也可以,那么编译器如何识别方法由类可以调用,那么就通过@classmethod进行区分),也可通过对象名调用,例如:
class Test():
#通过classmethod来使得编译器认识该方法是类方法
@classmethod
def classMethod(cls):
return "classMethod"
if __name__ == "__main__":
test = Test()
print(Test.classMethod())
print(test.classMethod())
输出:
classMethod
classMethod
静态方法:形式为def staticMethod(***),该方法与其他方法一样,并不需要self,但是需要在方法名上加上@staticmethod使得编译能认识该方法,该方法可直接通过类名或对象名调用,例如:
class Test():
@staticmethod
def staticMethod(name):
return "staticMethod " + name
@classmethod
def classMethod(cls):
return "classMethod"
if __name__ == "__main__":
test = Test()
print(Test.staticMethod("can incoming argument"))
print(test.staticMethod("can incoming argument"))
输出:
staticMethod can incoming argument
staticMethod can incoming argument
2. 类中属性
在类中可有:类属性、对象属性、私有属性三种属性之分。
类属性:该属性属于类所有,可直接通过类名进行引用,也可通过对象名进行引用。类属性修改可通过类名在类外引用进行修改,但是不要试图通过对象名引用去修改类属性,如果通过对象名修改类属性,则会创建一个与类属性同名的对象属性,此时同名的类属性和对象属性存储地址不一样,例如:
class Test():
classAttribute = 0
if __name__ == "__main__":
test = Test()
print("输出未修改类属性值的类属性地址:")
print("类名引用类属性的内存地址:"+ str(id(Test.classAttribute)))
print("对象名引用类属性的内存地址:"+ str(id(test.classAttribute)))
Test.classAttribute = 1
#并没有对象属性classAttribute,此时通过对象引用类属性进行修改
test.classAttribute = 2
print("输出修改类属性值的类属性的值和地址:")
print("类属性值:" + str(Test.classAttribute), "内存地址:"+ str(id(Test.classAttribute)))
print("对象属性值:" + str(test.classAttribute), "内存地址:"+ str(id(test.classAttribute)))
输出:
输出未修改类属性值的类属性地址:
类名引用类属性的内存地址:140724108055552
对象名引用类属性的内存地址:140724108055552
输出修改类属性值的类属性的值和地址:
类属性值:1 内存地址:140724108055584
对象属性值:2 内存地址:140724108055616
对象属性:该属性只能通过对象进行引用,通过构造函数__init__(self, ***)进行初始化,在构造函数中通过self进行引用的属性就是对象属性(注:很多情况下会看到对象属性与构造函数的形参名一样,其实只是命名问题,并不需要名字是一致的),并通过创建对象时进行对对象属性赋值,例如:
class Test():
classAttribute = 0
def __init__(self, name, ageAttribute):
#两个名字一致只是命名习惯而已
self.name = name
#对象属性无需跟构造函数__init__中形参名一致
self.age = ageAttribute
if __name__ == "__main__":
#通过创建对象时初始化对象属性的值,self不需要显性传参
test = Test("python_3", 10)
print(test.name, test.age)
输出:
python_3 10
3. 类的继承