为对象定义类
类定义对象的特征和行为 python会在运行时为每个对象赋予一个独特的id来辨识这个对象 数据域:即变量or实例变量,用于描述对象的状态,例如圆的radius,矩形的height和width python采用方法来定义对象的行为,方法亦即函数 python类使用变量存储数据域,定义方法来完成行为,类定义了对象的数据域和方法,对象是类的一个实例(对象就是实例,实例就是对象),可以创建一个类的多个对象,创建类的实例的过程称为实例化。
定义类
初始化程序:__ init __(双下划线) 语法
class ClassName:
initializer
methods
import math
class Circle:
def __init__(self, radius = 1):
self.radius = radius
def getPerimeter(self):
return 2*self.radius*math.pi
def getArea(self):
return self_.radius*self.radius*math.pi
def setRadius(self, radius):
self.radius = radius
构造对象
在内存中为类创建一个对象 调用类的__init__方法来初始化对象 所有方法,包括初始化程序,都有第一个参数self,指向调用方法的对象。 __ init __方法中的self参数被自动地设置为引用刚被创建的对象,我们可以为这个参数设置任何名字,但是经常使用的就是self。 构造方法: 类名(参数) 1.在内存中创建类的对象(实例) 2.调用类的 __ init __方法初始化对象,self参数自动设置为引用刚创建的对象
访问对象成员
访问一个对象的数据域并调用对象的方法 语法:
objectRefVar = ClassName(arguments)
可以使用圆点运算符(.)访问对象的数据域并调用他的方法,也被称为对象成员访问运算符。
objectRefVar.datafield
objectRefVar.method(args)
>>> from circle import circle
>>> c = circle(5)
>>> c.radius
5
>>> c.getPerimeter()
31.415926
>>> c.getArea()
78.539816
>>>
如果我们创建对象不需要被变量引用的话,那么就不用特意赋值给变量了。
print("Area is", Circle(5).getArea())
这种方式创建的对象叫做匿名对象。
self参数
self是指向对象本身的参数,可以用self访问在类中定义的对象成员,包括实例变量和实例方法。 一旦实例变量被创建,那么它的作用域就是整个类,例如self.x
def ClassName:
def __init__(self, ...):
self.x = 1
...
def m1(self, ...):
self.y = 2
...
z = 5
...
def m2(self, ...):
self.y = 3
...
u = self.x + 1
self.m1(...)
在创建类之后,使用类的程序被称作类的客户端,注意模块化编程。 看似保存一个对象的变量实际上包含了指向这个对象的引用,并且从严格上来说,变量和对象是不同的,但是大多数情况,两者的差别微乎其微,所以可以直接说 某变量是一个类的对象,而不用说 某变量是一个变量,它包含了一个指向类的对象的引用。
不变对象和可变对象
from Circle import Circle
def main():
myCircle = circle()
m = 5
printAreas(mycircle, n)
print("\nRadius is", myCircle.radius)
print("n is", n)
def printAreas(c, times):
print("Radius \t\tAreas")
while times >= 1:
print(c.radius, "\t\t", c.getArea())
c.radius = c.radius + 1
times = times - 1
main()
首先在Circle.py中创建了Circle类,在main程序中传递了myCircle和一个int对象n,调用了printAreas方法。 将对象传递给函数,即将对象的引用传递给函数,但是可变对象与不可变对象之间存在区别。
- 像数字和字符串这样的不可变的对象参数,函数外对象的原始值是不发生变化的;
- 像圆这样的可变对象参数,如果对象的内容在函数内被改变,那么对象的原始值也会变;
c.radius在+1的时候,就创建了新的int对象,并将其赋值给radius,在printAreas函数执行完毕之后,c.radius是6,即myCircle.radius的值是6,times-1会不断的更新times的值,但是外部的n,值还是5
隐藏数据域
定义:使数据域私有来保护数据,让类更容易维护 直接通过c.radius来访问数据域是可行的,例如c = Circle(5),但是,存在风险。
- 首先数据域存在被篡改的风险,例如对于设定了限定值的变量,人为的将其修改为新值,可能不合法;
- 类会变得难以维护且易于出错,例如在人为的对类中的数据域进行修改之后,那么我们需要确保在修改之后,其他使用这个类的程序在使用时,相应的数据域是正确的,e.g. c.radius需要为正值,人为的设定为负值之后,别的程序将无法使用
为了避免直接修改数据域,请不要让客户端能够直接访问数据域!!! 称为数据隐藏! 可以通过定义 私有数据域 来实现,私有方法 也可以定义,只需在变量与方法的前面加上 双下划线. 那么如果我们确实需要访问私有数据域,该怎么办呢? 需要提供方法来获取以及修改数据域,即get方法与set方法。 get方法头:
def getPropertyName(self):
get方法返回值是布尔值时:
def isPropertyName(self):
set方法头
def setPropertyName(self, propertyValue):
下面我们将之前的circle程序做一下修改:
import math
class Circel:
def __init__(self, radius = 1):
self.__radius = radius
def getRadius(self):
return self.__raidus
def getPerimeter(self):
return 2 * self.__radius * math.pi
def getArea(self):
return self.__radius * self.__radius * math.pi
>>> from CircleWidthPrivateRadius import Circle
>>> c = Circle(5)
>>> c.__radius
AttributeError: no attribute '__radius'
>>>> c.getRadius()
5
>>>
可以发现,因为__radius是私有的,无法直接访问,只能调用方法getradius()来访问。 如果类是被设计来给其他程序使用的,那么防止数据域被篡改易于维护,可以将数据域定为私有的,如果只是在程序内部使用,那么不用隐藏数据域
类的抽象与封装
抽象:是将类的实现和类的使用分离开来,类也被成为抽象数据类型(ADT) 类:是方法以及对这些方法要完成动作的描述的一个集合,被用来作为给客户端的类合约 封装:将数据和方法整合到一个单一的对象中并对用户隐藏数据域和方法的实现 用户本身并不需要知道类是如何实现的,类是为许多不同用户的使用而设计的,因此,我们针对类开发实例时,首先使用该类创建一个对象,并且尽量使用类的方法,当我们熟练掌握类的使用时,那么学习去实现一个类,也会更容易!
面向对象设计的思考
面向过程的设计:数据与操作是分离的,重点在于设计函数 面向对象的程序设计:数据与操作都是放在对象中的
UML类图
对类模板和对象的阐释可以使用UML(统一建模语言) 来规范 在UML类图中,数据域的表示如下: dataFieldName: dataFieldType 构造方法如下: ClassName(parameterName: parameterType) 方法如下: methodName(parameterName: parameterType): returnType
Circle
radius: float
Circle(radius = 1:float)
getArea() : float
getPerimeter() : float
setRadius(radius: float) : None
|