Skip to content

5. 类与对象

类是用于生成对象的模板。对象被称为类的实例

  

5.1. 一个对象类


程序 (classes_01)


# -*- coding=utf-8 -*-

class Objet:
    """une classe Objet vide"""

# 任何变量在构造时都可能具有属性
obj1=Objet()
obj1.attr1="un"
obj1.attr2=100
# 显示对象
print "objet1=[{0},{1}]".format(obj1.attr1,obj1.attr2)
# 修改对象
obj1.attr2+=100
# 显示对象
print "objet1=[{0},{1}]".format(obj1.attr1,obj1.attr2)
# 将对象1的引用赋值给对象2
obj2=obj1
# 修改对象2
obj2.attr2=0
# 显示这两个对象
print "objet1=[{0},{1}]".format(obj1.attr1,obj1.attr2)
print "objet2=[{0},{1}]".format(obj2.attr1,obj2.attr2)

注:

  • 第4行:另一种形式的注释。这种以三个“#”开头的注释可以跨多行;
  • 第3-4行:一个空对象类;
  • 第7行:实例化类Objet
  • 第17:引用复制。变量obj1和obj2是指向同一对象的两个指针(引用)。

结果

1
2
3
4
objet1=[un,100]
objet1=[un,200]
objet1=[un,0]
objet2=[un,0]

5.2. 一个 Person 类


程序 (classes_02)


# -*- coding=utf-8 -*-

class Personne:
    # 类属性
    # 未声明 - 可动态创建

    # 方法
    def identite(self):
        # 表面上看,使用了不存在的属性
        return "[{0},{1},{2}]".format(self.prenom,self.nom,self.age)

# ---------------------------------- main
# 属性为 public 且可动态创建
p=Personne()
p.prenom="Paul"
p.nom="Langevin"
p.age=48
# 调用方法
print "personne={0}\n".format(p.identite())

注:

  • 第 3-10 行:一个包含一个方法的类;
  • 第8行:类的所有方法的首个参数必须是对象self,该对象表示当前对象。

结果

personne=[Paul,Langevin,48]

5.3. 带有构造函数的 Personne 类


程序 (classes_03)


# -*- coding=utf-8 -*-

class Personne:
    
    # 构造函数
    def __init__(self,prenom,nom,age):
        self.prenom=prenom;
        self.nom=nom;
        self.age=age;

    # 方法
    def identite(self):
        # 表面上看,使用了不存在的属性
        return "[{0},{1},{2}]".format(self.prenom,self.nom,self.age)

# ---------------------------------- main
# 一个 Personne 对象
p=Personne("Paul","Langevin",48)
# 调用方法
print "personne={0}\n".format(p.identite())

注:

  • 第 6 行:类的构造函数名为 __init__。与其他方法一样,其第一个参数是 self;
  • 第 18 行:使用该类的构造函数创建了一个 Personne 对象。

结果

personne=[Paul,Langevin,48]

5.4. Personne 类,其构造函数中包含有效性检查


程序 (classes_04)


# -*- coding=utf-8 -*-

import re

# -------------------
# 一个专有异常类
class MyError(Exception):
    pass

class Personne:
    
    # 构造函数
    def __init__(self,prenom="x",nom="x",age=0):
         # 名字字段不能为空
        match=re.match(r"^\s*(\S+)\s*$",prenom)
        if not match:
            raise MyError("Le prenom ne peut etre vide")
        else:
            self.prenom=match.groups()[0]
        # 姓氏不能为空
        match=re.match(r"^\s*(\S+)\s*$",nom)
        if not match:
            raise MyError("Le nom ne peut etre vide")
        else:
            self.nom=match.groups()[0]
        # 年龄必须有效
        match=re.match(r"^\s*(\d+)\s*$",str(age))
        if not match:
            raise MyError("l'age doit etre un entier positif")
        else:
            self.age=match.groups()[0]

    def __str__(self):
        return "[{0},{1},{2}]".format(self.prenom,self.nom,self.age)
      
# ---------------------------------- 主
# 一个 Personne 对象
try:
    p=Personne("  Paul  ","  Langevin  ",48)
    print "personne={0}".format(p)
except MyError, erreur:
    print erreur
# 另一个 Personne 对象
try:
    p=Personne("  xx  ","  yy  ","  zz")
    print "personne={0}".format(p)
except MyError, erreur:
    print erreur
# 另一位人员
try:
    p=Personne()
    print "personne={0}".format(p)
except MyError, erreur:
    print erreur

注:

  • 第 7-8 行:一个从类 Exception 派生的类 MyError。它并未为后者添加任何功能,仅用于定义一个专属异常;
  • 第13行:构造函数的参数具有默认值。因此,操作 p=Personne() 等同于 p=Personne("x","x",0);
  • 第15-19行:分析参数prenom。该参数必须不为空。若不满足此条件,则抛出MyError异常并显示错误信息;
  • 第21-25行:姓名参数的处理方式同上;
  • 第27-31行:验证年龄;
  • 第 33-34 行:函数 __str__ 取代了之前名为 identite 的方法;
  • 第38-42行:实例化一个人,然后显示其身份信息;
  • 第 39 行:实例化;
  • 第 40 行:显示。该操作要求将对象 p 以字符串形式显示出来。 Python解释器会自动调用方法p.__str__()(如果该方法存在)。该方法的作用与Java或.NET语言中的方法toString()相同;
  • 第41-42行:处理可能出现的MyError类型异常。随后显示与该异常相关的错误信息;
  • 第44-48行:针对使用错误参数实例化的第二个人物,处理方式相同;
  • 第50-54行:第三个使用默认参数实例化的人员,处理方式同上。

结果

1
2
3
personne=[Paul,Langevin,48]
l'age doit etre un entier positif
personne=[x,x,0]

5.5. 添加一个充当第二个构造函数的方法


程序 (classes_05)


# -*- coding=utf-8 -*-

import re

# -------------------
# 一个拥有者异常类
class MyError(Exception):
    pass

class Personne:
    # 类的属性
    # 未声明 - 可动态创建
    
    # 构造函数
    def __init__(self,prenom="x",nom="x",age=0):
         # 名字字段不能为空
        match=re.match(r"^\s*(\S+)\s*$",prenom)
        if not match:
            raise MyError("Le prenom ne peut etre vide")
        else:
            self.prenom=match.groups()[0]
        # 姓必须不为空
        match=re.match(r"^\s*(\S+)\s*$",nom)
        if not match:
            raise MyError("Le nom ne peut etre vide")
        else:
            self.nom=match.groups()[0]
            # 年龄必须有效
        match=re.match(r"^\s*(\d+)\s*$",str(age))
        if not match:
            raise MyError("l'age doit etre un entier positif")
        else:
            self.age=match.groups()[0]

    def initWithPersonne(self,p):
        # 使用人员 p 初始化当前对象
        self.__init__(p.prenom,p.nom,p.age)

    def __str__(self):
        return "[{0},{1},{2}]".format(self.prenom,self.nom,self.age)
      
# ---------------------------------- main
# 一个 Personne 对象
try:
    p0=Personne("  Paul  ","  Langevin  ",48)
    print "personne={0}".format(p0)
except MyError, erreur:
    print erreur
# 另一个 Personne 对象
try:
    p1=Personne("  xx  ","  yy  ","  zz")
    print "personne={0}".format(p1)
except MyError, erreur:
    print erreur
# 另一个Personne对象
p2=Personne()
try:
    p2.initWithPersonne(p0)
    print "personne={0}".format(p2)
except MyError, erreur:
    print erreur

注:

  • 与前一个脚本的区别在于第35-37行。我们添加了方法 initWithPersonne。该方法调用了构造函数 __init__。 与类型化语言不同,本语言无法通过参数或返回值的性质来区分同名方法。因此,无法通过不同的参数(此处为 Personne 类型的对象)来创建多个构造函数。

结果

1
2
3
personne=[Paul,Langevin,48]
l'age doit etre un entier positif
personne=[Paul,Langevin,48]

5.6. 一个 Personne 对象列表


程序 (classes_06)


# -*- coding=utf-8 -*-

class Personne:
    ...

# ---------------------------------- 主
# 创建人员对象列表
groupe=[Personne("Paul","Langevin",48), Personne("Sylvie","Lefur",70)]
# 这些人员的身份
for i in range(len(groupe)):
  print "groupe[{0}]={1}".format(i,groupe[i])

注:

  • 第3-5行:已描述的类 Personne

结果

groupe[0]=[Paul,Langevin,48]
groupe[1]=[Sylvie,Lefur,70]

5.7. 创建一个从 Personne 类派生的类


程序 (classes_07)


# -*- coding=utf-8 -*-

import re

class Personne:
    ...

class Enseignant(Personne):
    def __init__(self,prenom="x",nom="x",age=0,discipline="x"):
        Personne.__init__(self,prenom,nom,age)
        self.discipline=discipline

    def __str__(self):
        return "[{0},{1},{2},{3}]".format(self.prenom,self.nom,self.age,self.discipline)
  

# ---------------------------------- 主
# 创建“Person”对象及其派生对象的列表
groupe=[Enseignant("Paul","Langevin",48,"anglais"), Personne("Sylvie","Lefur",70)]
# 这些人员的身份
for i in range(len(groupe)):
  print "groupe[{0}]={1}".format(i,groupe[i])

注:

  • 第 5-6 行:已定义的类 Personne
  • 第 8 行:声明类 EnseignantPersonne 类的派生类
  • 第10行:派生类Enseignant的构造函数必须调用父类Personne的构造函数;
  • 第 21-22 行:为了显示 groupe[i],解释器使用方法 groupe[i].__str__()。 所使用的 __str__() 方法属于 groupe[i] 的实际类,如下面的结果所示。

结果

groupe[0]=[Paul,Langevin,48,anglais]
groupe[1]=[Sylvie,Lefur,70]

5.8. 创建一个从 Personne 类派生的第二个类


程序 (classes_08)


# -*- coding=utf-8 -*-

import re

class Personne:
    ...

class Enseignant(Personne):
    ...
  
class Etudiant(Personne):
    def __init__(self,prenom="x",nom="x",age=0,formation="x"):
        Personne.__init__(self,prenom,nom,age)
        self.formation=formation

    def __str__(self):
        return "[{0},{1},{2},{3}]".format(self.prenom,self.nom,self.age,self.formation)

# ---------------------------------- 主
# 创建“Person”对象及其派生对象的列表
groupe=[Enseignant("Paul","Langevin",48,"anglais"), Personne("Sylvie","Lefur",70), Etudiant("Steve","Boer",22,"iup2 qualite")]
# 这些人的身份
for i in range(len(groupe)):
  print "groupe[{0}]={1}".format(i,groupe[i])

注:

  • 此脚本与前一个类似。

结果

groupe[0]=[Paul,Langevin,48,anglais]
groupe[1]=[Sylvie,Lefur,70]
groupe[2]=[Steve,Boer,22,iup2 qualite]