第九章 面向对象-3
我们再定义一个有实际意义的类,员工类Worker,想想要包含哪些属性哪些行为,或者以后我们都采用专业的说法,这个员工类包含哪些数据域和哪些方法。Have a try! 下面是我的程序:
class Worker:
# Construct a Worker object
def __init__(self, name="xiaoming",workID="2020011001",department="市场部",age=23,salary=2000):
self.name = name
self.department = department
self.salary = salary
def main():
w1=Worker(name="小红",salary=3000)
print(w1.salary)
main()
假设我是小红的朋友,想给小红多点钱,在main函数里加一句代码:
w1.salary=40000
print(w1.salary)
或者在程序中还有很多函数f1……f10,每个函数都可以修改小红的薪水,这就导致了混乱。因为修改薪水这件事只能由老板决定,而且还有可能程序修改的薪水值不小心写错成上千万,我们都无法控制这类情况,所以这个问题需要解决。也就是现在存在的问题是直接访问对象的数据域导致数据被篡改,而且类会变得难以维护并且易于出错,因此在Python中引入数据隐藏的概念,目的是防止直接访问数据域。
数据隐藏可以通过定义私有数据域来完成,在Python中,私有数据域使用两个下划线定义,如self.__ salary,还可以以两个下划线开始来定义私有方法。私有数据域只能在类内访问,类外不可以访问。按照数据隐藏的思路,上述程序可以改成下面的样子:
class Worker:
# Construct a Worker object
def __init__(self, name="xiaoming",workID="2020011001",department="市场部",age=23,salary=2000):
self.name = name
self.department = department
self.__salary = salary
def main():
w1=Worker(name="小红",salary=3000)
print(w1.__salary) #程序运行时此处会出现错误
#错误提示:AttributeError: 'Worker' object has no attribute '__salary'
main()
错误的原因是因为__salary是私有成员,类外不能访问,而类内可以访问,如setSalary方法中修改了__salary。我把main函数修改成下面这样再运行一下试试:
def main():
w1=Worker(name="小红",salary=3000)
w1.__salary=40000
天哪!居然没有错误,Why?
把main函数修改成下面这样再运行一下:
def main():
w1=Worker(name="小红",salary=3000)
w1.__salary=40000
print(w1.__salary)
又出错了,我 我 我 ,

这是为什么呢?Keep reading!
在Python中,有以下几种方式来定义变量:
xx:公有变量。类内类外均可访问
_xx:单前置下划线,私有化属性或方法,类对象和子类可以访问,禁止导入(我们已经学习过导入,如果想用random里面的方法,就需要导入random)from somemodule import *
__xx:双前置下划线,私有化属性或方法,无法在外部直接访问(名字重新调整所以访问不到)
__xx__:双前后下划线,系统定义名字(不要自己发明这样的名字)
xx_:单后置下划线,用于避免与Python关键词的冲突
“名字重新调整所以访问不到” 是什么鬼?就是在类内定义的私有成员如__salary在类外被重新命名为_Worker__salary,所以print(w1.__salary) 会提示没有__salary属性,即类外不提供访问。那既然改了名字,我可以用改过的名字访问吗?回答可以!看看下面的运行结果:

虽然_Worker__salary被高亮显示提示有问题,但是确实输出了结果3000!等一下,再看看有没有问题!给你1分钟时间。
w1.__salary=40000 这行代码为什么没有错误,为什么输出的不是40000?
呃,This is an extraordinarily interesting question.I must think fast.
再加一行代码 print(w1.__salary) 输出试一下,结果如下:

这是因为类的实例可以动态增加属性和方法,也就是说Worker类里面又增加了一个__salary数据域。但是建议不要这样做。由此可见,Python是有多么任性!下面引入一个例子,看一下不明白也没有关系!
class Person(object):# object这个问题下一节解释(9.2继承与多态)
count = 0 #此变量为类变量,每个对象都可以访问,类Person也可以访问(深入了解请自学)
def __init__(self, name):
self.name = name
# 实例化
p1 = Person("tom")
print(p1.name) # tom
print(p1.count) # 0
p2 = Person("jack")
print(p2.name) # jack
print(p2.count) # 0
#
# 通过对象修改类变量
print("*******通过对象修改类变量*********")
p1.count = 2
print(p1.count) # 2
print(p2.count) # 0
print(Person.count) # 0
#
# # 通过类修改类变量
print("*******通过对象修改类变量*********")
Person.count = 3
print(p1.count) # 2
print(p2.count) # 3
print(Person.count) # 3
#
# 给对象增加属性
print("*******给对象增加属性age*********")
p1.age = 23
print(p1.age) # 23
#
# 给对象增加方法
print("*******给对象p1增加方法set_age*********")
def set_age(self, age):
self.age = age
from types import MethodType
p1.set_age = MethodType(set_age, p1)
p1.set_age(25)
print(p1.age) # 25
print("*******对象p2没有方法set_age*********")
# print(p2.age)
# AttributeError: 'Person' object has no attribute 'age'
运行结果如下:
tom
0
jack
0
*******通过对象修改类变量*********
2
0
0
*******通过对象修改类变量*********
2
3
3
*******给对象增加属性age*********
23
*******给对象p1增加方法set_age*********
25
*******对象p2没有方法set_age*********
Process finished with exit code 0
如果去掉倒数第二行# print(p2.age) 的 # 符号,则运行结果如下:
tom
0
jack
0
*******通过对象修改类变量*********
2
0
0
Traceback (most recent call last):
*******通过对象修改类变量*********
2
3
File "F:/教学/2019秋/Python code/first.py", line 45, in <module>
3
*******给对象增加属性age*********
print(p2.age)
23
*******给对象p1增加方法set_age*********
AttributeError: 'Person' object has no attribute 'age'
25
*******对象p2没有方法set_age*********
Process finished with exit code 1
Ok,这个问题就说到这儿,但是还有一个问题,类内不能访问私有成员,那这些私有成员还有什么用?如何使用类的这些成员呢?
注意:是类外不能访问,类内是可以的,所以可以定义方法,一般是一对setter和getter。在方法里访问这些成员,然后类外调用这些方法就可以了。那这不是更费事吗?当然不是。我们可以给允许外界访问的成员定义一对setter和getter,不允许外界修改的就不定义了,所以一切尽在我掌控!
定义有setter和getter方法的Worker类如下,试着理解一下:
class Worker:
# Construct a Worker object
def __init__(self, name="xiaoming",workID="2020011001",department="市场部",age=23,salary=2000):
self.name = name
self.department = department
self.__salary = salary
def setSalary(self, salary):
self.__salary = salary
def getSalary(self):
return self.__salary
def main():
w1=Worker(name="小红",salary=3000)
print(w1.name," salary:",w1.getSalary())
if w1.getSalary()<5000:
w1.setSalary(5500)
print("涨工资了!")
print(w1.name, " salary:", w1.getSalary())
main()