在Class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,这样,就隐藏了内部的复杂逻辑。
但是,从前面的Student类定义来看,外部代码还是可以自由地修改一个实例的name,score属性
>>> bart.score 59 >>> bart.score=99 >>> bart.score 99
如果要让内部属性不被外部访问,可以把属性的名称前面加上两个下划线__,在python中,实例的变量名如果以__开头,就变成了私有变量(private),只有内部可以访问,外部不能访问
>>> class Student(object): ... def __init__(self,name,score): ... self.__name=name ... self.__score=score ... def print_score(self): ... print('%s:%s' %(self.__name,self.__score)) ... >>> bart=Student('Zhangsan',59) >>> bart.__name Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Student' object has no attribute '__name'
这样就确保了外部代码不能随意修改对象内部的状态,这样通过访问限制的包含,代码更加健壮
如果外部代码想要获取name和score怎么办?可以给类Student增加get_name和get_score方法
class Student(object): def __init__(self,name,score): self.__name=name self.__score=score def print_score(self): print('%s:%s' %(self.__name,self.__score)) def get_name(self): return self.__name def get_score(self): return self.__score
通过bart.get_name()即可返回对应name
如果又要允许外部代码修改score怎么办?可以再给Student类增加set_score方法
class Student(object): def __init__(self,name,score): self.__name=name self.__score=score def print_score(self): print('%s:%s' %(self.__name,self.__score)) def get_name(self): return self.__name def get_score(self): return self.__score def set_score(self,score): self.__score=score
set_score除了self还包含一个参数score修改方法为
bart.set_score(99)
原先直接通过bart.score=99也可以直接修改,为什么要定义一个方法大费周折?因为在方法中,可以对参数进行检查,避免传入无效的参数
class Student(object): def __init__(self,name,score): self.__name=name self.__score=score def print_score(self): print('%s:%s' %(self.__name,self.__score)) def get_name(self): return self.__name def get_score(self): return self.__score def set_score(self,score): if 0<=score<=100: self.__score=score else: raise ValueError('bad score')
如果传递了无效的成绩会报错
Traceback (most recent call last): File "Student.py", line 28, in <module> bart.set_score(101) File "Student.py", line 15, in set_score raise ValueError('bad score') ValueError: bad score
注意,在python中,变量名类似__xxx___,也就是以双下划线开头并且以双下划线结尾的是特殊变量,特殊变量是可以直接访问的,不是private变量,所以不能用__name__,__score__这样的变量名。
有些时候,你会看到以一个下划线开头的实例变量名,比如_name
,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。
双下划线开头的实例变量是不是一定不能从外部访问呢?其实也不是。不能直接访问__name
是因为Python解释器对外把__name
变量改成了_Student__name
,所以,仍然可以通过_Student__name
来访问__name
变量:
>>> bart._Student__name 'Zhangsan'
但是强烈建议你不要这么干,因为不同版本的Python解释器可能会把__name
改成不同的变量名。
注意下面这种错误写法
>>> bart=Student('Zhangsan',59) >>> bart.get_name() 'Zhangsan' >>> bart.__name='Lisi' >>> bart.__name 'Lisi'
表面上看,外部代码“成功”地设置了__name
变量,但实际上这个__name
变量和class内部的__name
变量不是一个变量!内部的__name
变量已经被Python解释器自动改成了_Student__name
,而外部代码给bart
新增了一个__name
变量。不信试试:还是原来的Zhangsan
>>> bart.get_name() 'Zhangsan'
练习:
class Student(object): def __init__(self, name, gender): self.name = name self.gender = gender
class Student(object): def __init__(self,name,gender): self.__name=name self.__gender=gender def get_gender(self): return self.__gender def set_gender(self,gender): if gender == 'male' or gender == 'female': self.__gender=gender else: raise ValueError('bad gender性别为male或者female') bart=Student('Zhangsan','male') print(bart.get_gender()) bart.set_gender('femalde') print(bart.get_gender())
如果传递的性别参数不是male或者female则报错
Traceback (most recent call last): File "Student2.py", line 14, in <module> bart.set_gender('femalde') File "Student2.py", line 11, in set_gender raise ValueError('bad gender性别为male或者female') ValueError: bad gender性别为male或者female