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.name12 13 use_class=MyClass('xiaoqiang')14 print('调用类的属性:',use_class.i)15 print('调用类的方法:',use_class.f())
#程序执行结果如下:
1 ================= RESTART: C:/Users/DL He/Desktop/类的构造方法.py =================2 调用类的属性: 1233 调用类的方法: 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, in4 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:\Python\workspace\datatime\20171121>python __init__()方法_1.py2 我是不带参数的__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:\Python\workspace\datatime\20171121>python __init__()方法_1.py2 我是带一个参数的__init__方法,参数值为: hello3 类实例化结束
#由执行结果看到,调用的是带了一个param参数的构造方法,若把类的实例化语句更改为:
1 DefaultIinit()
#执行结果为:
1 D:\Python\workspace\datatime\20171121>python __init__()方法_1.py2 Traceback (most recent call last):3 File "__init__()方法_1.py", line 12, in4 DefaultInit()5 TypeError: __init__() missing 1 required positional argument: 'param'
#或更改为:
1 DefaultInit('hello','world')
#执行结果为:
1 D:\Python\workspace\datatime\20171121>python __init__()方法_1.py2 Traceback (most recent call last):3 File "__init__()方法_1.py", line 12, in4 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:\Python\workspace\datatime\20171121>python __init__()方法_1.py2 我是不带参数的__init__方法。3 类实例化结束
#由执行结果看到,调用的构造方法除了self外,没有其他参数。若把类的实例化语句更改为如下:
1 DefaultInit('hello')
#执行结果如下:
1 D:\Python\workspace\datatime\20171121>python __init__()方法_1.py2 Traceback (most recent call last):3 File "__init__()方法_1.py", line 13, in4 DefaultInit('hello')5 TypeError: __init__() takes 1 positional argument but 2 were given
#或更改为:
1 DefaultInit('hello','world')
#执行结果如下:
1 D:\Python\workspace\datatime\20171121>python __init__()方法_1.py2 Traceback (most recent call last):3 File "__init__()方法_1.py", line 13, in4 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=017 print('修改后分数:',stu.score)18 stu.info()
#程序执行结果如下:
1 D:\Pythonworkspace>python 类的访问权限.py2 修改前分数: 963 学生:xiaoming;分数:964 修改后分数: 05 学生: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=017 print('修改后分数:',stu.__score)18 stu.info()
#程序执行结果如下:
1 D:\Pythonworkspace>python 类的访问权限.py2 Traceback (most recent call last):3 File "类的访问权限.py", line 14, in4 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.__score15 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 类的访问权限.py2 修改前分数: 963 学生:xiaoming;分数:964 修改后分数: 965 学生: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.__score15 16 def set_score(self,score):17 self.__score=score18 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 类的访问权限.py2 修改前分数: 963 学生:xiaoming;分数:964 修改后分数: 05 学生: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.__score15 16 def set_score(self,score):17 self.__score=score18 19 if 0<=score<=100:20 self.__score=score21 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 修改前分数:962 学生:xiaoming;分数:963 请输入0到100的数字。4 修改后分数:965 学生: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, in10 pri_pub.__foo()11 AttributeError: 'PrivatePublicMethod' object has no attribute '__foo'
#由输出结果看到,私有方法和私有变量类似,不能通过外部调用。