一个类将一个具体概念抽象化(如轿车可以抽象为一个车辆类,一辆轿车便是车辆类的一个实例)
整理重组: 各种数据(默认数据类型或另一个类),使其适应各种该类的实列各种数据的处理方法(方法),使其通用于所有该类的实例 允许: 限制外部对类的字段的访问控制外部对类的字段的修改术语
UML 图表示
语法
声名一个类
注意
属性的 UML 图表示
属性的名称和类型必须写出
注意 1:类型
UML 图是独立于编程语言的,其属性的类型不一定强制对应于 Python 中的数据类型
Ex :integer( UML ) = int( Python ), real( UML ) = float( Python )
注意 2:多重性
一个属性可以表示同类型的多个对象,如下3种情况:
我们知道元素数量的最大值和最小值:
+nomAttribut :typeAttribut[valMin..valMax]
+nomAttribut: typeAttribut[val](最大值与最小值相等)
我们知道元素数量的最小值:
+nomAttribut :typeAttribut[valMin..*]
不知道元素的个数:
nomAttribut :typeAttribut[*]
矩阵的表示:nomAttribut :typeAttribut[lMin..lMax, cMin..cMax]
注意 3
类的方法
UML 图表示方法
-calculerAire( ) : real ⇒ \Rightarrow ⇒ 一个方法 calculerAire 可见性为私有,无参数,返回值为实数
+afficherSommet( x : integer ) ⇒ \Rightarrow ⇒ 一个方法 afficherSommet 可见性为公有,有一个为整型 x 的参数,无返回值
示例
注意 :
UML 图表示构造函数
为了表示一个构造函数:
我们可以套用类的方法的模板给构造函数命名为类的名称语法:+ <<create>> NomClasse( parametres )
声名一个构造函数
示例
UML:
+ <<creat>> Vehicule( ) ⇒ \Rightarrow ⇒ Vehicule 类的无参数构造函数
+ <<creat>> Personne( nom : String, prenom : String ) ⇒ \Rightarrow ⇒ Personne 类的构造函数,参数为两个字符串
Python:
class Vehicule : # 无参数构造函数 def __init__( self ) : # ... class Personne : # 参数为两个字符串的构造函数 def __init__( self, nom, prenom ) : # ...以上两种类完整的 UML 图和代码:
Vehicule 类:
2个属性:anne 和 marque2个方法:afficher 和 calculerAge import datetime class Vehicule : def __init__( self, anne, marque ) : self.anne = anne self.marque = marque def afficher( self ) : print( "Voiture de marque ", self.marque, " fabrique en ", self.anne ) def calculerAge( self ) : now = datetime.datetime.now( ) return now.year - self.annePersonne 类:
3个属性:nom,prenom 和 dateNaissance2个方法:afficher 和 calculerAge import datetime class Personne : def __init__( self, nom, prenom, dateNaissance ) : self.nom = nom self.prenom = prenom self.dateNaissance = dateNaissance def afficher( self ) : print( self.prenom, " ", self.nom ) def calculerAge( self ) : now = datetime.datetime.now( ) return now.year - self.dateNaissance.year注意 :
在类的方法中访问类的属性或调用类中其他方法可以使用 self.保护数据和其处理方法
封装概括为:
尽可能隐藏类字段以限制其被外部访问。
目的:
保护和控制类的属性的值 ⇒ \Rightarrow ⇒ 隐藏一些数据和一些处理方法(封装)以避免类外的方法可以修改/使用它们
名称UMLPython可访问性公共+无无限制保护#名称前加 _只能其本身与子类进行访问私有-名称前加 __只允许这个类本身访问可访问性:
我们可以通过区分符自己定义一个类每一部分的可访问性可访问性的选择依靠各属性和方法的性质 如车辆类中,方法如“发动” → \rightarrow → +(公共)方法如“点燃火花塞”,“加油” → \rightarrow → -(私有) 公共方法 = 功能接口 / 对象的可见部分:负责访问其他的部分访问属性
封装的主要原则:
对属性的访问(读或写)是否要受到控制或禁止 ⇒ \Rightarrow ⇒ 私有属性: 汽车的变速杆的位置必须在1至6档之间学生的分数必须在0到100分之间登录银行账户的密码 如果属性的内部实现与它的表示不对应 ⇒ \Rightarrow ⇒ 私有属性: 某压强以单位 mmHg 保存,但是要以单位 Pa 输出注意 :
对象本身(即其中包含的方法)始终可以直接访问其属性(即使它们是私有的)。Getter,Setter 和 Property:
好处: 受控的从外部访问私有属性如果内部实现发生改变而不会影响其他类(如:在 Vehicule 类中不储存车辆的生产年份,而是保存1900年与生产日期之差)在读/写后加入调用所需的其他函数(如:在一个复数更新了它的实部后计算其极坐标表达式) 它们的实现不是必须的(如:不会读/写一个银行密码)Getter 方法:
以只读的形式访问(即显示)没有任何的参数输入(除了self)在声名方法前一行加装饰器:@propertySetter 方法:
受控的以写的方式访问(即修改)有一个参数输入(除了 self)在声名方法前一行加装饰器:@nomAttribut.setter注意:
在 Python 中,getter 和 setter 都叫 property约定俗成的,它们的名称为其对应的属性示例:
""" 这种写法很不 Python """ class Vehicule : def __init__( self, annee, marque ) : # 内部实现:与1900之差 if ( annee > 1900 ) and ( annee <= now.year ) : self.__annee = annee - 1900 self.marque = marque注意 :以上的写法不是 Python 的风格(不要这么写!)
# 很 Python 的写法 class Vehicule : def __init__( self, annee, marque ) : # 内部实现:与1900之差 self.annee = annee # 私有属性并调用 setter # 访问不受控制 => 公共属性 self.marque = marque @property def annee( self ) : # 将其转为同名只读属性的 getter 方法 return self.__annee + 1900 @annee.setter def annee( self, annee ) : if ( annee > 1900 ) and ( annee <= now.year ) : self.__annee = annee - 1900优点:如果可见的 annee 属性在公共区域被改变,仍无需修改构造函数
一些其他的例子:
class Velo : # ... @property def numVitesse( self ) : return self.__numVitesse @numVitesse.setter def numVitesse( self, num ) : if ( num > 0 ) and ( num < 7 ) : self.__numVitesse = num class Date : # ... @property def jour( self ) : return self.__jour @jour.setter def jour( self, jour ) : if ( jour > 0 ) and ( jour < 31 ) : self.__jour = jour return True # 修改成功 return False # 修改失败UML 类图
目的:
描述一个应用中的各种类和它们之间的静态关系类之间的关系
关联(association):当一个类知道另一个类时,可以用关联。
如企鹅需要知道气候的变化,需要了解气候规律,企鹅和气候就是关联关系。关联关系用实线箭头来表示聚合(aggregation): 聚合表示一种弱的拥有关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分
如大雁和雁群,每只大雁都是属于一个雁群,一个雁群可以有多只大雁聚合关系用空心的菱形 + 实线箭头来表示组合(composition): 是一种强的拥有关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样
如鸟和其翅膀就是组合关系,因为它们是部分和整体的关系,并且翅膀和鸟的生命周期是相同的组合关系用实习的菱形 + 实线箭头来表示 注意:合成关系的连线两端还有一个数字1和数字2,这被称为基数。表明这一端的类可以有几个实例,很显然,一个鸟应该有两只翅膀
对象定义:
对抽象描述的具体实现一个对象(可类比为一个变量) = = = 一个类(可类别为基本数据类型)的实例一个对象(如具体的一辆车)是由一个类(如车这个大类)创建(实例化)而来的每个类都有一个“建造蓝图”(即构造函数)用以实例化它的各个对象类和对象的区别: 一个对象的存在周期:
创建:调用构造函数:var = NomClasse( parametres )使用:调用方法,读/写其中的属性删除:调用析构函数 此步骤可省略(隐式的调用析构函数)显式的调用析构函数:del var为了创建/实例化一个对象,必须:
将通过调用构造函数创建的对象存储在一个变量中:该变量指向新对象示例:
v1 = Vehicule( ) # 创建一个 Vehicule 类的对象 # 创建一个 Personne 类的对象(以及一个 Date 类的) p1 = Personne( "Norris", "Chuck", Date( 10, 3, 1940 ) )隐式的析构函数:Garbage Collector
原则:
在 Python 中,当不再有对一个对象的引用时,可以确定该程序将不再能够访问它
⇒ \Rightarrow ⇒ 对象将进入回收序列, 内存空间将被释放(但并不总是立即释放)
示例:
""" 我们可以直接显式的调用析构函数来释放内存 """ v = Vehicule( 2014, "Renault" ) # 使用 v 的代码 # ... # 删除对象 v del v # ...示例:
v1 = Vehicule( ) v1.marque = "Renault" # 此操作支持对公共属性进行 v1.annee = 2014 # 或有 setter 方法的私有属性 v1.afficher( ) # 显示:"Vehicule de marque Renault fabrique en 2014"示例:车辆类:
对于一辆车,我们有如下:
数据 品牌生产年份 数据处理 计算车龄(现在年份 - 生产年份)输出显示和车辆有关的信息(品牌,生产年份) 限制 车的生产年份必须在1900年到今年分析:
抽象为一个车辆类2个属性 marque:字符串,公共annee:整数,私有 2个方法(不算构造函数) calculerAge:无输入参数,返回一个整型数,公共afficher:无输入参数,无返回值,公共画出 UML 图: 在 Python 中声名 Vehicule 类:
# 导入包含类中需要用到的函数的模块 from datetime import datetime class Vehicule : # 1)声名构造函数 def __init__( self, marque, annee = datetime.now( ).year ) : self.marque = marque self.annee = annee # 年份为默认的今年 # 2)声名 property @property def annee( self ) : return self.__annee + 1900 @annee.setter def annee( self, annee ) : anneeNow = datetime.now( ).year if ( annee > 1900 ) and ( annee <= anneeNow ) : self.__annee = annee - 1900 # 3)声名方法 def afficher( self ) : res = "Vehicule" res += " de marque " + self.marque res += " fabrique en " + str( self.annee ) print( res ) def calculerAge( self ) : anneeNow = datetime.now( ).year return anneeNow - self.annee解决方法:
使用默认值(如前述 Vehicule 类中默认年份为今年)使用类方法一个类方法是一种可以被一个类或一个对象(不推荐!)调用的方法:
要在声名类方法的前一行使用装饰器:@classmethod类方法的第一个参数为 cls 而不是 self对于构造函数,它调用该类的构造函数,并将构造函数的结果(即新的对象)返回注意: 因为它适用于该类,所以它不能访问对象的属性
示例:
from datetime import datetime class Vehicule : # "正常的"构造函数:所有参数都已知 def __init__( self, annee, marque ) : self.annee = annee self.marque = marque # 类方法(第二种构造函数) @classmethod def formMarqueOnly( cls, marque ) : annee = datetime.now( ).year # annee 的值为默认今年(缺啥补啥) return cls( annee, marque ) # 调用类的构造函数 # ... v1 = Vehicule.formMarqueOnly( "Renault" ) v1.afficher( ) # 输出为:Vehicule de marque Renault de 2019默认有,操作符 == 比较两个对象的地址,但不比较两者的值/属性
v1 = Vehicule( "Peugeot", 2014 ) v2 = Vehicule( "Peugeot", 2014 ) print( v1 == v2 ) # 输出 "False"__eq__ 方法:
所有的对象都有 __eq__ 方法,它可以比较两个对象是否一样:
语法:def __eq__( self, other )
相等性测试通过一下步骤:
先确保比较的对象是存在的(即不能是None )且要和被比较的对象是同类(关键字:isinstance)比较两个对象是否指向同一个地址(地址相同则是同一个对象,关键字:is)比较两个对象的各种属性注意:
如果比较的属性类型是类,则还必须在这些类中实现 __eq__ 方法只有当一个类中实现了 __eq__方法,它才能调用== 操作符示例:
def __eq__( self , other ) : # 1/ 我们先比较该对象是否为 Vehicule 类 if other not isinstance( Vehicule ) : return False # 2/ 地址相同 => 相同的对象 if self is other : return True # 到此处我们已知该对象是 Vehicule 类 # 3/ 我们比较两个对象的属性 ( marque 和 annee ) if self.marque != other.marque : return False if self.annee != other.annee : return False # 所有属性都相等 => 对象相等 return True v1 = Vehicule( "Peugeot", 2014 ) v2 = Vehicule( "Peugeot", 2014 ) print( v1 == v2 ) # 输出 "True"__str__ 方法:
所有的对象都有 __str__ 方法,它可以将对象转化为字符串:语法:def __str__( self )如果被转换的属性也是一个类,那个类中同样也要实现 __str__ 方法在 UML 图中,我们使用 +toString( ) : String 表示该方法示例:
def __str__( self ) : res = " Vehicule " res += " de marque " + self.marque res += " fabrique en " + str( self.annee ) return res v1 = Vehicule( 2014 , " Peugeot " ) print( v1 , "qui a", v1.calculerAge( ), " ans " ) # " Vehicule de marque Peugeot fabrique en 2014 qui a 5 ans "聚合:构造函数可以采用已经创建的对象(在这种情况下为共享对象),也可以自己创建对象(通过调用构造函数)
组合:为了防止属性被共享,构造函数总是调用构造函数(即该类的一个属性是个类)来创建其属性。(同生共死)
示例: 聚合:
class Violoniste : def __init__( self , violon ) : self.violon = violon # violon 是一个已经被创建的对象 @classmethod def fromViolon( cls , d, l ) : violon = Violon( d, l ) # 创建了一个新的 violon return cls( violon ) # 调用 violoniste 的构造函数组合:
class Violon : def __init__( self , d, l ) : # 直接调用属性的构造函数(同生共死) self.cordes = [ Corde( d[i], l[i] ) for i in range(4) ]Getter & Setter:
个人意见:仅针对组合为了防止属性被共享,不要创建 getter 和 setter