目录
面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度。
面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。
面向对象的设计思想是从自然界中来的,在自然界中类(class)
和实例(instance)
的概念是很自然的。
来个实例就明白了:
# !user/bin/env python
# coding=utf-8
__author__ = "zjw"
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def print_information(self):
print self.name, self.score
if __name__ == '__main__':
stu = Student("Kangkang", 12)
stu.print_information()
输出:
Kangkang 12
Class
是一种抽象概念,比如我们自定义一个学生(Student)类,包含学生姓名(name)和成绩(score)的类。
Instance
则是一个个具体的Student,比如stu
就是一个具体的Student
对象。
例2.1:
# !user/bin/python
# coding=utf-8
__author__ = "zjw"
# 定义的一个Person类
class Person(object):
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
def print_information(self):
print self.name, self.age, self.gender
if __name__ == '__main__':
# 实例化一个新的Person
person = Person("Michael", 20, "男")
person.print_information()
输出:
Michael 20 男
分析:
class
关键字后面跟的是一个类名,即Person
,类名通常规定大写字母开头。(object)
表示该类继承自object
类。通常没有合适的继承类,就直接继承object
类,这是所有类最终都会继承的类。__init__
是一个特殊方法,前后都有两个下划线。__init__
方法的第一个参数永远是self
,表示创建的实例本身,在__init__
方法内部,我们就可以将各种属性绑定到self
。
__init__
方法,创建实例时就不能传入空的参数了,必须传入与该方法匹配的参数,self
不需要传入。def __init__(self, **pro)
来进行传入的传递,或则直接不写__init__
方法。数据封装
。Person
类中定义一个print_information
方法,用于实例后的具体对象访问自身的属性内容。当然我们可以通过在外面写一个函数来实现,但是Person
实例本身就有这些数据,难道我们的个人信息需要别人来帮我们介绍吗,况且有些信息我们还想保密起来的,所以说访问类内部的属性就没必要从外面的函数去访问,可以直接在Person
类内部定义一个方法实现,即我们上面定义的一个print_information
方法,这样,我们就把数据给封装起来了。print_information
函数,是因为该封装数据的函数是和Person
类本身是关联起来的,所以称之为类的方法
。name
,age
和gender
三个参数,而打印这个实例的各个属性,都是Person
内部数据封装
的事情了,这些数据和逻辑被“封装”起来了,调用起来容易,且外部调用者不需要考虑内部实现的细节。在Class内部,有属性和方法,而外部的代码可以直接通过变量直接访问实例的方法来操作数据,这样我们可以影藏内部的复杂逻辑。
我们来看上面的Person类,因为没有多加限制,所以我们访问属性时,可以直接通过实例化后的变量名.属性名就可以直接访问和修改内部的属性值,按上面的思考这是很不合理的。如果想让内部的属性不被外部访问,这就涉及到了作用域
,前面我们已经学到作用域的相关知识,是在变量前面加上两个下划线,即__
。
因此根据作用域
的相关知识,我们运用到类内部就是在原来定义的属性名前面加上两个下划线__
,即属性就变成一个类内部的私有属性,即java中的private型。
说了这么多,我们何不改变一下上面的Person类来试一下咯。
实例3.2.1:
# !user/bin/python
# coding=utf-8
__author__ = "zjw"
# 定义的一个Person类
class Person(object):
def __init__(self, name, age, gender):
self.__name = name
self.__age = age
self.__gender = gender
def print_information(self):
print self.__name, self.__age, self.__gender
if __name__ == '__main__':
# 实例化一个新的Person
person = Person("Michael", 20, "男")
person.print_information()
输出:
Michael 20 男
分析:可以看到,我将__init__
方法内部的属性都改成了私有属性,当然我们在外部实例的person对象调用print_information
方法是没有问题,但是如果我们通过person.__name
,那么很遗憾系统会报错。不妨来试一下吧。
Traceback (most recent call last):
File "G:/PyCharm-workspace/Python_Learning/person.py", line 23, in
person.__name
AttributeError: ‘Person‘ object has no attribute ‘__name‘
这些是用到person.__name
时的报错信息。
改进思想:
刚刚我将各个属性改成了private型,那么我们该如何访问和修改实例的属性值呢?这就和java中的setter和getter方法一个道理了。我们可以写相应的方法来访问和修改相应的属性。
经过修改后的Person
类是下面这样子的:
# !user/bin/python
# coding=utf-8
__author__ = "zjw"
# 定义的一个Person类
class Person(object):
def __init__(self, name, age, gender):
self.__name = name
self.__age = age
self.__gender = gender
def print_information(self):
print self.__name, self.__age, self.__gender
def get_name(self):
return self.__name
def get_age(self):
return self.__age
def get_gender(self):
return self.__gender
def set_name(self, name):
self.__name = name
def set_age(self, age):
self.__age = age
def set_gender(self, gender):
self.__gender = gender
if __name__ == '__main__':
# 实例化一个新的Person
person = Person("Michael", 20, "男")
# 我们先打印一下此时的person实例
person.print_information()
# 然后我们在通过相应的setter方法修改一下person实例中的相应属性值
person.set_name("Jane")
person.set_age(12)
person.set_gender("女")
# 最后我们在通过相应的getter方法来获得此时person类中的相应属性值
# 我们用print来打印getter方法获得的属性值
print person.get_name(), person.get_age(), person.get_gender()
输出:
Michael 20 男
Jane 12 女
分析:我们可以看到,上面的Person
类中,为三个属性name,age和gender都添加了相应的setter
和getter
方法,那么在main函数中我们就可以通过设置好的setter,getter方法自如的访问和修改person类中的相应的属性值。
当我们将属性值设置成private型时,我们是否就无法通过直接获得属性值了,其实并不是,我们可以通过下面的方法获得。依旧以上面的例子延续下来,我们可以通过_Person__name
来获得此时的person
类的__name
属性值。来试一下吧(修改一下上面的main函数):
例3.3.1:
if __name__ == '__main__':
# 实例化一个新的Person
person = Person("Michael", 20, "男")
print person._Person__name
输出:
Michael
可以看到我们通过_Person__name
变量来获取到了__name的属性值,冒充了get_name()
方法,真是罪不可恕,浑水摸鱼的做法啊,所以这种方法是非常不推荐,应该说是禁止使用的。且在编译器中,我们会看到一些提示信息也建议我们修改掉这种用法。Python解释器本身不会阻止我们干这种坏事,一切全靠我们自觉咯。
接下来再说另一个错误示范(同样是修改了上面的main方法):
例3.3.2:
if __name__ == '__main__':
# 实例化一个新的Person
person = Person("Michael", 20, "男")
# 我们先打印一下此时person的__name
print person.get_name()
# 我们通过一种错误的示范来修改__name值
person.__name = "NewName"
# 再次打印__name值
print person.get_name()
# 通过person.__name来打印__name值试一下
print person.__name
输出:
Michael
Michael
NewName
分析:可以看到,上面的实例中我们通过person.__name = "NewName"
该操作来修改__name
属性值,但是两次通过get_name
方法获得的值确实相同的,而最后我们通过person.__name
来取__name属性值时,获得的却是改变后的值,这是为什么呢?
其实表面上,我们是成功的修改了__name
变量,但实际上__name
变量和此时的class内部的__name
属性并不是同一变量!原来内部的变量被解释器解改成了_Person__name
,而外面我们通过该方法修改变量则是我们新设置的新的变量,所以修改的并非同一个变量,所以两次通过getter方法获得的__name
属性值是相同的。
所以得出结论,我们还是强烈不建议使用该方法获得和修改类中的相应的属性值。
__
前缀实现的Python显得更加的简洁,也使我们编写的时候显得更加的明然,直接通过变量名即可判断属性和方法的公开性。__init__
(这里暂且叫他构造函数吧),只要设置了相应的传入参数,那么在外部实例化时,则必须传入相对应的参数,也不能无参实例化,这可能是一个小小的弊端,或则说是学到这里,我并没有找到更好的方法来解决这个问题,只有上面提到的在__init__
函数的参数里用一个**pro
参数,即关键字参数
。我的一个猜想是可能Python的缔造者为了尽量简化Python代码,限制了传入的参数后,下面我们不必再一个个修改,反而增加了代码的行数。原文:https://www.cnblogs.com/vanishzeng/p/12238860.html