1、类的构造方法
#在介绍之前,我们对前面的示例做一些改动,代码如下:
1 #! /usr/bin/python3 2 #-*-coding:UTF-8-*- 3 #类的构造方法 4 5 class MyClass(object): 6 i=123 7 def __init__(self,name): #注意!这里是个坑:“__init__”的“__”符号是左右两个! 8 self.name=name 9 10 def f(self): 11 return 'hello,'+self.name 12 13 use_class=MyClass('xiaoqiang') 14 print('调用类的属性:',use_class.i) 15 print('调用类的方法:',use_class.f())
#程序执行结果如下:
1 ================= RESTART: C:/Users/DL He/Desktop/类的构造方法.py ================= 2 调用类的属性: 123 3 调用类的方法: hello,xiaoqiang
#若类的实例化语句写法和之前一样,即:
1 use_class=MyClass()
#程序执行结果如下:
1 ================= RESTART: C:/Users/DL He/Desktop/类的构造方法.py ================= 2 Traceback (most recent call last): 3 File "C:/Users/DL He/Desktop/类的构造方法.py", line 13, in <module> 4 use_class=MyClass() 5 TypeError: __init__() missing 1 required positional argument: 'name'
#从代码和输出结果看到,实例化MyClass类时调用了__init__()方法。我们在代码中并没有指定调用__init__()方法,怎么会报__init__()方法错误呢?
#在Python中,__init__()方法是一个特殊方法,在对象实例化时会被调用。__init__()的意思时初始化,是initialization的简写。这个方法的书写方式是:先输入两个下划线,后面接着init,再接着两个下划线。这个方法也叫构造方法。在定义类时,若不显式地定义一个__init__()方法,则程序默认调用一个无参的__init__()方法。比如以下两段代码的使用效果是一样的:
#代码一:
1 #! /usr/bin/python3 2 #-*-coding:UTF-8 -*- 3 #__init__方法 4 5 class DefaultInit(object): 6 def __init__(self): 7 print('类实例化时执行我,我是__init__方法。') 8 9 def show(self): 10 print('我是类中定义的方法,需要通过实例化对象调用。') 11 12 test=DefaultInit() 13 print('类实例化结束。') 14 test.show()
#程序执行结果如下:
1 ================ RESTART: C:/Users/L/Desktop/__init__()方法.py ================ 2 类实例化时执行我,我是__init__方法。 3 类实例化结束。 4 我是类中定义的方法,需要通过实例化对象调用。
#代码二:
1 #! /usr/bin/python3 2 #-*-coding:UTF-8 -*- 3 #__init__方法_1 4 5 class DefaultInit(object): 6 def show(self): 7 print('我是类中定义的方法,需要通过实例化对象调用。') 8 9 test=DefaultInit() 10 print('类实例化结束。') 11 test.show()
#程序执行结果如下:
1 ================ RESTART: C:/Users/L/Desktop/__init__()方法.py ================ 2 类实例化结束。 3 我是类中定义的方法,需要通过实例化对象调用。
#由上面的两段代码的输出结果看到,当代码中定义了__init__()方法时,实例化类时会调用该方法;若没有定义__init__()方法,实例化类时也不会报错,此时调用默认的__init__()方法。
#在Python中定义类时若没有定义构造方法(__initI__()方法),则在类的实例化时系统调用默认的构造方法。另外,__init__()方法可以有参数,参数通过__init__()传递到类的实例化操作上。
#既然__init__()方法是Python中的构造方法,那么是否可以在类中定义多个构造方法呢?我们来看下面3段代码:
#代码一:
1 #! /usr/bin/python3 2 #-*-coding:UTF-8 -*- 3 #__init__()方法_1 4 5 class DefaultInit(object): 6 def __init__(self): 7 print('我是不带参数的__init__方法。') 8 9 DefaultInit() 10 print('类实例化结束')
#程序执行结果如下:
1 D:Pythonworkspacedatatime20171121>python __init__()方法_1.py 2 我是不带参数的__init__方法。 3 类实例化结束
#在只有一个__init__()方法时,实例化没有什么顾虑。
#代码二:
1 #! /usr/bin/python3 2 #-*-coding:UTF-8 -*- 3 #__init__()方法_1 4 5 class DefaultInit(object): 6 def __init__(self): 7 print('我是不带参数的__init__方法。') 8 9 def __init__(self,param): 10 print('我是带一个参数的__init__方法,参数值为:',param) 11 12 DefaultInit('hello') 13 print('类实例化结束')
#程序执行结果如下:
1 D:Pythonworkspacedatatime20171121>python __init__()方法_1.py 2 我是带一个参数的__init__方法,参数值为: hello 3 类实例化结束
#由执行结果看到,调用的是带了一个param参数的构造方法,若把类的实例化语句更改为:
1 DefaultIinit()
#执行结果为:
1 D:Pythonworkspacedatatime20171121>python __init__()方法_1.py 2 Traceback (most recent call last): 3 File "__init__()方法_1.py", line 12, in <module> 4 DefaultInit() 5 TypeError: __init__() missing 1 required positional argument: 'param'
#或更改为:
1 DefaultInit('hello','world')
#执行结果为:
1 D:Pythonworkspacedatatime20171121>python __init__()方法_1.py 2 Traceback (most recent call last): 3 File "__init__()方法_1.py", line 12, in <module> 4 DefaultInit('hello','world') 5 TypeError: __init__() takes 2 positional arguments but 3 were given
#由执行结果看到,实例化类时只能调用带两个占位参数的构造方法,调用其它构造方法都会报错。
#代码三:
1 class DefaultInit(object): 2 def __init__(self,param): 3 print('我是带一个参数的__init__方法,参数值为:',param) 4 5 def __init(self): 6 print('我是不带参数的__init__方法。') 7 8 DefaultInit() 9 print('类实例化结束')
#程序执行结果如下:
1 D:Pythonworkspacedatatime20171121>python __init__()方法_1.py 2 我是不带参数的__init__方法。 3 类实例化结束
#由执行结果看到,调用的构造方法除了self外,没有其他参数。若把类的实例化语句更改为如下:
1 DefaultInit('hello')
#执行结果如下:
1 D:Pythonworkspacedatatime20171121>python __init__()方法_1.py 2 Traceback (most recent call last): 3 File "__init__()方法_1.py", line 13, in <module> 4 DefaultInit('hello') 5 TypeError: __init__() takes 1 positional argument but 2 were given
#或更改为:
1 DefaultInit('hello','world')
#执行结果如下:
1 D:Pythonworkspacedatatime20171121>python __init__()方法_1.py 2 Traceback (most recent call last): 3 File "__init__()方法_1.py", line 13, in <module> 4 DefaultInit('hello','world') 5 TypeError: __init__() takes 1 positional argument but 3 were given
#由执行结果看到,实例化类时只能调用带一个占位参数的构造方法,调用其他构造方法都会报错。
#由以上几个实例我们得知,一个类中可定义多个构造方法,但实例化类时只实例化最后的构造方法,即后面的构造方法会覆盖前面的构造方法,并且需要根据最后一个构造方法的形式进行实例化。所以建议一个类中只定义一个构造函数。
2、类的访问权限
#在类内部有属性和方法,外部代码可以通过直接调用实例变量的方法操作数据,这样就隐藏了内部的复杂逻辑,例如:
1 #! /usr/bin/python3 2 #-*-coding:UTF-8-*- 3 #类的访问权限 4 5 class Student(object): 6 def __init__(self,name,score): 7 self.name=name 8 self.score=score 9 10 def info(self): 11 print('学生:%s;分数:%s'%(self.name,self.score)) 12 13 stu=Student('xiaoming',96) 14 print('修改前分数:',stu.score) 15 stu.info() 16 stu.score=0 17 print('修改后分数:',stu.score) 18 stu.info()
#程序执行结果如下:
1 D:Pythonworkspace>python 类的访问权限.py 2 修改前分数: 96 3 学生:xiaoming;分数:96 4 修改后分数: 0 5 学生:xiaoming;分数:0
#由代码和输出结果看到,在类中定义的非构造方法可以调用类中构造方法实例变量的属性,调用的方式为self.实例变量属性名,如代码中的self.name和self.score。可以在类的外部修改类的内部属性。如果要让内部属性不被外部访问,该如何操作?
#要让内部属性不被外部访问,可以在属性名称前加两个下划线__。在Python中,实例的变量名如果以__开头,就会变成私有变量(private),只有内部可以访问,外部不能访问。据此,我们可以把student类改一改:
1 #! /usr/bin/python3 2 #-*-coding:UTF-8-*- 3 #类的访问权限 4 5 class Student(object): 6 def __init__(self,name,score): 7 self.__name=name 8 self.__score=score 9 10 def info(self): 11 print('学生:%s;分数:%s'%(self.__name,self.__score)) 12 13 stu=Student('xiaoming',96) 14 print('修改前分数:',stu.__score) 15 stu.info() 16 stu.__score=0 17 print('修改后分数:',stu.__score) 18 stu.info()
#程序执行结果如下:
1 D:Pythonworkspace>python 类的访问权限.py 2 Traceback (most recent call last): 3 File "类的访问权限.py", line 14, in <module> 4 print('修改前分数:',stu.__score) 5 AttributeError: 'Student' object has no attribute '__score'
#由执行结果看到,我们已经无法从外部访问实例变量的属性__score了。
#这样可以确保外部代码不能随意修改对象内部的状态,通过访问限制的保护,代码更加安全。比如上面的分数对象是一个比较重要的内部对象,如果外部可以随便更改这个值,大家都随便更改自己成绩单中的分数,是比较危险的。
#如果外部代码要获取类中的name和score要如何操作呢?
#在Python中,可以为类增加get_attrs方法,获取类中的私有变量,例如上面的示例中添加get_score(name的使用方式类同)方法,代码如下:
1 #! /usr/bin/python3 2 #-*-coding:UTF-8-*- 3 #类的访问权限 4 5 class Student(object): 6 def __init__(self,name,score): 7 self.__name=name 8 self.__score=score 9 10 def info(self): 11 print('学生:%s;分数:%s'%(self.__name,self.__score)) 12 13 def get_score(self): 14 return self.__score 15 16 stu=Student('xiaoming',96) 17 print('修改前分数:',stu.get_score()) 18 stu.info() 19 print('修改后分数:',stu.get_score()) 20 stu.info()
#由执行结果如下:
1 D:Pythonworkspace>python 类的访问权限.py 2 修改前分数: 96 3 学生:xiaoming;分数:96 4 修改后分数: 96 5 学生:xiaoming;分数:96
#由执行结果看到,通过get_score方法已经可以正确得到类内部的属性值。
#是否可以通过外部更改内部私有变量的值?
#在Python中,可以为类增加set_attrs方法,修改类中的私有变量,如更改上面示例中的score属性值,可以添加set_score(name使用方式类同)方法,代码如下:
1 #! /usr/bin/python3 2 #-*-coding:UTF-8-*- 3 #类的访问权限 4 5 class Student(object): 6 def __init__(self,name,score): 7 self.__name=name 8 self.__score=score 9 10 def info(self): 11 print('学生:%s;分数:%s'%(self.__name,self.__score)) 12 13 def get_score(self): 14 return self.__score 15 16 def set_score(self,score): 17 self.__score=score 18 19 stu=Student('xiaoming',96) 20 print('修改前分数:',stu.get_score()) 21 stu.info() 22 stu.set_score(0) 23 print('修改后分数:',stu.get_score()) 24 stu.info()
#程序执行结果如下:
1 D:Pythonworkspace>python 类的访问权限.py 2 修改前分数: 96 3 学生:xiaoming;分数:96 4 修改后分数: 0 5 学生:xiaoming;分数:0
#由程序执行结果看到,通过set_score方法正确更改了变量score的值。这里有个疑问,原先stu.score=0这种方式也可以修改score变量,为什么又要那么费劲定义私有变量,set.score的意义在于哪里呢?
#在Python中,通过定义私有变量和对应的set方法可以帮助我们做参数检查,避免传入无效的参数,如对上面的示例更改如下:
1 #! /usr/bin/python3 2 #-*-coding:UTF-8-*- 3 #类的访问权限 4 5 class Student(object): 6 def __init__(self,name,score): 7 self.__name=name 8 self.__score=score 9 10 def info(self): 11 print('学生:%s;分数:%s'%(self.__name,self.__score)) 12 13 def get_score(self): 14 return self.__score 15 16 def set_score(self,score): 17 self.__score=score 18 19 if 0<=score<=100: 20 self.__score=score 21 else: 22 print('请输入0到100的数字。') 23 24 25 stu=Student('xiaming',96) 26 print('修改前分数:',stu.get_score()) 27 stu.info() 28 stu.set_score(-10) 29 print('修改后分数:',stu.get_score()) 30 stu.info()
#程序执行结果如下:
1 修改前分数:96 2 学生:xiaoming;分数:96 3 请输入0到100的数字。 4 修改后分数:96 5 学生:xiaoming;分数:96
#由输出结果看到,调用set_score方法时,如果传入的参数不满足条件,就按照不满足条件的程序逻辑执行。
#既然类有私有变量,那么有没有私有方法呢?
#是的,类也有私有方法。类的私有方法也是以两个下划线开头,声明该方法为私有方法,且不能在类外使用。私有方法的调用方式如下:
self.__private_methods
#我们通过下面的示例,进一步了解私有方法的使用:
1 #! /usr/bin/python3 2 #-*-coding:UTF-8-*- 3 #类的私有方法 4 5 class PrivatePublicMethod(object): 6 def __init__(self): 7 pass 8 9 def __foo(self): #(私有方法) 10 print('这是私有方法。') 11 12 def foo(self): 13 print('这是公共方法。') 14 print('这是公共方法中调用私有方法。') 15 self.__foo() 16 print('公共方法调用私有方法结束。') 17 18 pri_pub=PrivatePublicMethod() 19 print('开始调用公共方法:') 20 pri_pub.foo() 21 print('开始调用私有方法:') 22 pri_pub.__foo()
#程序执行结果如下:
1 D:Pythonworkspace>python 类的私有方法.py 2 开始调用公共方法: 3 这是公共方法。 4 这是公共方法中调用私有方法。 5 这是私有方法。 6 公共方法调用私有方法结束。 7 开始调用私有方法: 8 Traceback (most recent call last): 9 File "类的私有方法.py", line 22, in <module> 10 pri_pub.__foo() 11 AttributeError: 'PrivatePublicMethod' object has no attribute '__foo'
#由输出结果看到,私有方法和私有变量类似,不能通过外部调用。