⑤ 类函数,成员函数与静态函数
「类函数」:用于访问类属性,使用@classmethod装饰器
来修饰,第一个参数是cls,类本身,用于调用类属性,但是不能访问实例属性。类方法可以通过类直接调用,或通过实例直接调用。但无论哪种调用方式,最左侧传入的参数一定是类本身!!!代码示例如下:
class A: @classmethod def fun_a(cls): print(type(cls), cls) if __name__ == '__main__': A.fun_a() a = A() a.fun_a()
运行结果如下:
<class 'type'> <class '__main__.A'> <class 'type'> <class '__main__.A'>
「成员函数和类实例绑定」,类实例化后才能调用,它的第一个参数表示实例本身,一般用self表示,成员函数可以直接操作对象内部的数据。如果使用类直接调用成员函数,需要显式地将实例作为参数传入。代码示例如下:
class B: def fun_b(self): print("Call fun_b()") if __name__ == '__main__': b = B() b.fun_b() B.fun_b(b) # 类调用成员函数需将实例传入
运行结果如下:
Call fun_b() Call fun_b()
「静态函数」,在定义上面的fun_b函数的时候,智能提示里就有一个Make method static的选项,对于这种不需要self参数的函数(无需实例参与),都可以定义成静态函数,调用过程中无需将类实例化。使用@staticmethod装饰器
来声明,通过 类名.函数名
或 实例.函数名
进行调用,代码示例如下:
class C: @staticmethod def fun_c(): print("Call fun_c()") if __name__ == '__main__': C.fun_c() c = C() c.fun_c()
运行结果如下:
Call fun_c() Call fun_c()
⑥ 访问控制
所谓的访问控制,就是「类的属性和方法是公有还是私有」,如果属性和方法只能在类内部访问,而不能被实例访问的话,我们就称这个属性或方法为私有的。
Python和其他编程语言不同,没有类似于public和private这样的访问权限修饰符,而是采用一种「名字改编技术」。默认公有,而私有的属性名和方法名会加上两下划线,比如下面的
__skill
,当然这只是伪私有,改成了_类名私有属性/方法名
,比如下面调用people._Person__skill,是可以访问到私有成员的:
class People: sex = 1 # 类属性 __skill = "敲代码" # 私有类属性,只能类内部访问,外部无法访问 def speak(self): print("我是一个人,技能是:%s" % self.__skill, end='\t') people = People() people.speak() people.sex = -1 print("性别:" + ("男" if people.sex == 1 else "女")) print("访问私有属性:%s" % people._People__skill)
运行结果如下:
我是一个人,技能是:敲代码 性别:女 访问私有属性:敲代码
虽然可以这样访问到私有成员,但是不建议这样做!
另外还有一种「单下划线开头的变量名或方法名」,同样是私有成员,不过类和实例都能访问,也会被子类继承。如果你不想属性或方法被子类继承就还是用双下划线吧!还有一种「开头结尾都是双下划线的属性或函数」是类的特殊成员,有特殊用途,比如上面的
__init__
初始化方法;最后如果你「定义的变量和某个保留关键字冲突」的话,可以使用单下划线作为后缀,比如:in_ = 1。
⑦ 动态绑定
Python中可以「动态地为类或对象绑定属性或函数」。类动态绑定属性与函数,对该类的所有实例有效。代码示例如下:
class A: def __init__(self, id_): self.id_ = id_ # 定义一个用于动态绑定的函数 def set_name(self, name): print("调用了动态绑定的函数") self.name = name if __name__ == '__main__': # 动态绑定一个属性 A.kind = "人类" # 动态绑定一个函数 A.set_name = set_name a = A(1) # 类访问动态绑定的属性 print(A.kind) # 实例访问动态绑定的属性 print(a.kind) # 类访问动态绑定的函数 A.set_name(a,'123') # 实例访问动态绑定的函数 a.set_name('321')
运行结果如下:
人类 人类 调用了动态绑定的函数 调用了动态绑定的函数
实例动态绑定属性与函数,只对当前对象有效,对其他实例无效,需要用到一个MethodType类
,代码示例如下:
from types import MethodType class B: def __init__(self, id_): self.id_ = id_ # 定义一个用于动态绑定的函数 def set_name(self, name): print("调用了动态绑定的函数") self.name = name if __name__ == '__main__': b_1 = B('1') # 动态为实例1绑定一个属性 b_1.kind = "人类" # 动态为实例1绑定一个函数 b_1.set_name = MethodType(set_name, b_1) # 实例1设置动态绑定的属性与函数 print(b_1.kind) b_1.set_name('123') # 另一个类实例调用动态绑定的属性 b_2 = B('2') print(b_2.kind)
运行结果如下:
人类 Traceback (most recent call last): 调用了动态绑定的函数 File "/Users/jay/Project/Python/Book/Chapter 9/9_7.py", line 30, in <module> print(b_2.kind) AttributeError: 'B' object has no attribute 'kind'