基本概念
- OOP(Object Oriented Programming) 面对对象编程,对象可以是任何被识别的实体:学生、桌子、圆、按钮甚至贷款。
- 一个对象的特性就好比人的身份证,Python 运行时会自动对每个对象赋予一个独特的 id
- 一个对象的状态是用变量来表示的,称为数据域。
- Python 使用方法来定义一个对象的行为,function 也被称为函数,通过对对对象上的方法,可以让对象完成某项操作。例如对圆进行 getArea() 和 getPerimeter() 的方法。圆就可以调用 getArea() 方法返回它的面积。调用 getPerimeter()的方法来返回它的周长。
一个 Python 类使用变量存储数据域,定义方法来完成动作,类就是一份契约或者模板或者蓝本,它定义对象的数据域和方法。
对象是类的一个实例,一个类可以创建多个对象,创建类的过程也被称为实例化,它定义对象的数据域和方法。
定义类的方法:
__init__
这个方法称为初始化程序,它是在创建和初始化这个新对象时被调用的。初始化能够完成任何动作, 但是初始化程序被设计完成初始化动作,例如:初始值创建对象的数据域。
Python 定义类的语法:
class Classnames:
initalizer
methods
构造对象
一旦定义了一个类,就可以使用构造的方法来创建对象。构造对象完成两个任务:
- 在内存中为类创建一个对象。
- 调用类的__init__ 方法来初始化对象
包括初始化所有的方法,都有第一个参数 self,这个参数指向调用方法的对象。 __init__
方法中的 self 参数常被自动设置位引用刚被创建类的对象,你可以为这个参数指定任何的名字,但是按照惯例,经常使用的是 self。
class Circle:
# Construct a circle object
def __init__(self,radius = 1):
self.radius = radius
def getPerimeter(self):
return 2*self.radius*math.pi
def setRadius(self,radius):
self.radius = radius
这段程序创建了一个 Circle 的类,同时把自己的 r
默认设置为1,但是,可以通过后续的 setRadius 函数去设置它的半径,然后可以先定义一个对象设置为这种类,比如:
c = Circle(5)
print(c.radius)
print(c.getPerimeter())
print(c.getArea())
self 的作用:
定义每个方法的第一个参数就是 self。self 是指向对象本身的参数,它的作用域就是整个类,self.x 是一个在 __init__
方法中创建的实例变量。
一旦实例变量被创建。那么它的作用域就是整个类。self .x 是agiel在__init__
方法中创建的实例变量,它可以在 m2中被访问。实例变量 self.y 在方法 。
事实上,想要创建一个新的类,只需要以一个变量名 = 类的初始函数,即可创建。例如下面的代码尝试,当然还需要有一定的定义函数的基础,变量定义的基础。
# * conding = UTF8
# class learn
# author:Neverland
# 生活就是一坨屎
import math
class Circle:
# Construct a circle object
def __init__(self,radius = 1):
self.radius = radius
def getPerimeter(self):
return 2*self.radius*math.pi
def setRadius(self,radius):
self.radius = radius
CIrcle2 = Circle(25)
程序使用 Circle
类创建 Circle
对象,这种使用类的程序被称作类的客户端。
书中也提到,看似保存一个对象的变量实际上包含的是指向这个对象的引用。严格地讲变量和对象是不同的,但大多数情况下,这两者的区别是可以忽略的。所以,为了简单起见,最好说"circle1" 是一个 “Circle” 对象,而不会冗长地描述位 “circle1” 是一个变量,它包含一个指向 Circle
对象的引用。
UML 类图
Circle(radius=1:float)
getArea():float 构造方法
getPerimeter():float
setRadius(radius,float): none
类中方法定义中总有 self
参数,但在 UML 图中并不包括它,因为客户端并不知道这个参数会不会使用参数调用方法。
方法__init__
同样不需要罗列在 UML 图中,因为它被构造方法所调用,它的参数与构造方法的参数是一样的。
Plus:事实上,init 的构造函数就是self 加上一些常量定义,然后其他函数继续去调用它。
隐藏数据域
如果直接使用构造函数的话,数据是可以直接修改的,很容易造成数据被篡改,比如修改 Circle
类,想要保证 radius
保持在一个非负值,此时不仅仅需要修改 Circle
类,还得更改程序,因为客户端可以直接可以修改半径,导致其他 function 得出的值和预期不一致,所以需要私有数据域
私有数据与和方法可以在类内部被访问,但是他们不能在类外被访问,可以定义一个 get
的 function
以达到返回值的效果,然后再设置一个 set
以修改的值。
通过在属性名前加两个下划线,将 radius
属性定义位私有的来修改,例如修改之前的 Circle
类。
import math
class Circle:
# Construct a circle object
def __init__(self,radius = 1):
self.__radius = radius
def getPerimeter(self):
return 2*self.__radius*math.pi
def getRadius(self):
return self.__radius
def setRadius(self,radius):
self.__radius = radius
c = Circle(5)
print(c.__radius) #错误的类外访问方法
print(c.getRadius()) #正确的使用 get 的方法访问
如果仅仅使用print(c.__radius)
会报错,因为它是私有的
❗如果类是被设计给用来给其他程序使用的,为了防止数据被篡改并使类易于维护,就将数据定义为私有的。如果这个类仅仅在程序内部使用,就没有必要隐藏数据域。
❗使用两个下划线来命名私有数据域,但是不要以一个以上的下划线结尾。在 Python 中,以两个下划线开头同时以两个下划线结尾的名字有特殊的含义。例如 __radius
是一个私有数据域,但是 __radius__
并不是私有数据域
☑️关键点:类的抽象是将类的实现和类的使用分离的概念。类的实现和细节对用户而言是不可见的。这就是类的封装。