Skip to content

12. 类与对象

类是创建对象的模板。对象被称为类的实例

Image

注意[shared] 文件夹已放置在项目的 [Root Sources] 中。

12.1. 脚本 [classes_01]:一个 Object 类

[classes_01] 脚本演示了一种已过时的类用法:

#  a class
class Objet(object):
    """une classe Objet vide"""


#  any variable of type [Object] can have attributes by construction
obj1 = Objet()
obj1.attr1 = "un"
obj1.attr2 = 100
#  displays the
print(f"objet1=[{obj1}, {type(obj1)},{id(obj1)},{obj1.attr1},{obj1.attr2}]")
#  modify object
obj1.attr2 += 100
#  displays the
print(f"objet1=[{obj1.attr1},{obj1.attr2}]")
#  assign reference obj1 to obj2
obj2 = obj1
#  modifies the object pointed to by obj2
obj2.attr2 = 0
#  displays both objects - obj1 now points to a modified object
print(f"objet1=[{obj1.attr1},{obj1.attr2}]")
print(f"objet2=[{obj2.attr1},{obj2.attr2}]")
#  obj1 and obj2 point to the same object
print(f"objet1=[{obj1}, {id(obj1)},{obj1.attr1},{obj1.attr2}]")
print(f"objet2=[{obj2}, {id(obj2)},{obj2.attr1},{obj2.attr2}]")
print(obj1 == obj2)
#  instance type obj1
print(f"type(obj1)={type(obj1)}")
print(f"isinstance(obj1,Objet)={isinstance(obj1, Objet)}, isinstance(obj1,object)={isinstance(obj1, object)}")
#  any type is an object in Python
print(f"type(4)={type(4)}")
print(f"isinstance(4, int)={isinstance(4, int)}, isinstance(4, object)={isinstance(4, object)}")

  • 第 2-3 行:一个空的 [Object] 类;
  • 第2行:该类可以有三种声明形式:
    • class Object;
    • class Object();
    • class Object(object);
  • 第 3 行:另一种注释形式。这种注释前缀为三个 "",,可以跨多行;
  • 第 7 行:Object 类的实例化。结果是一个地址,如第 24–26 行所示;
  • 第 8–9 行:直接初始化对象的两个属性;
  • 第 17 行:复制引用。变量 obj1 obj2 是指向同一个对象的两个指针(引用);
  • 第 19 行:我们修改了 [obj2] 所指向的对象。由于 [obj1] [obj2] 指向同一个对象,第 21 行和第 22 行中对象 [obj1, obj2] 的显示结果将表明 [obj1] 所指向的对象已发生变化;
  • 第 24–26 行:这些行旨在证明变量 [obj1] [obj2] 相等。第 26 行的输出将证实这一点。在此比较中,[obj1] [obj2] 的地址是相等的;
  • 每个 Python 对象都通过表达式 [id(object)] 获取的唯一 ID 来标识。第 24 行和第 25 行将显示 [obj1] [obj2] 所指向的对象的 ID 完全相同,从而证明这两个引用指向同一个对象;
  • 第 27–29 行:函数 [isinstance(expr, Type)] 返回布尔值 True,若表达式 [expr] 的类型为 [Type]。在此,我们将看到 [obj1] 的类型为 [Object],这似乎很自然,但同时也属于 [object] 类型。 [object] 类是所有 Python 类的类。根据类继承的特性,子类 F 拥有其父类 P 的所有属性,且函数 [isinstance(F 的实例, P)] 返回 True;
  • 第 30–32 行:表明类型 [int] 也是类型 [object]。所有 Python 类型都继承自 [object] 类;

结果


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_01.py
objet1=[<__main__.Objet object at 0x0000025C3F469BB0>, <class '__main__.Objet'>,2595221838768,un,100]
objet1=[un,200]
objet1=[un,0]
objet2=[un,0]
objet1=[<__main__.Objet object at 0x0000025C3F469BB0>, 2595221838768,un,0]
objet2=[<__main__.Objet object at 0x0000025C3F469BB0>, 2595221838768,un,0]
True
type(obj1)=<class '__main__.Objet'>
isinstance(obj1,Objet)=True, isinstance(obj1,object)=True
type(4)=<class 'int'>
isinstance(4, int)=True, isinstance(4, object)=True
 
Process finished with exit code 0

12.2. 脚本 [classes_02]:Person 类

[classes_02] 脚本演示了类属性的公开性:它们可以直接从类外部访问。这是另一个不推荐的类使用示例。不过,我们仍将其纳入其中,因为你可能会偶尔遇到此类代码(Python 允许这样做),而你需要能够理解它。

#  person class
class Personne:
    #  class attributes
    #  undeclared - can be created dynamically

    #  method
    def identité(self: object) -> str:
        #  a priori, uses non-existent attributes
        return f"[{self.prénom},{self.nom},{self.âge}]"


#  ---------------------------------- main
#  attributes are public and can be created dynamically
#  class instantiation
p = Personne()
#  direct initialization of class attributes
p.prénom = "Paul"
p.nom = "de la Hûche"
p.âge = 48
#  call a method of the
print(f"personne={p.identité()}")
#  instance type p
print(f"type(p)={type(p)}")
print(f"isinstance(Personne)={isinstance(p, Personne)}, isinstance(object)={isinstance(p, object)}")

  • 第 2–9 行:包含方法的类;
  • 第 7 行:类的每个方法都必须将 self 对象(指当前对象)作为其第一个参数。[identity] 方法返回一个字符串;
  • 第 15 行:实例化一个 [Person] 对象;
  • 第 16–19 行:展示对象的属性可以动态创建(它们并不存在于类定义中);
  • 第 9 行:类的属性使用 [self.attribute] 这种表示法;
  • 第 23–24 行表明对象 [p] 既是 [Person] 类的实例,也是 [object] 类的实例;

结果


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_02.py
personne=[Paul,de la Hûche,48]
type(p)=<class '__main__.Personne'>
isinstance(Personne)=True, isinstance(object)=True
 
Process finished with exit code 0

12.3. 脚本 [classes_03]:带构造函数的 Person 类

脚本 [classes_03] 演示了类的标准用法:

#  person class
class Personne:
    #  builder - initializes three attributes
    def __init__(self: object, prénom: str, nom: str, âge: int):
        #  first name: person's first name
        #  name: person's name
        #  age: age of the person

        self.prénom = prénom
        self.nom = nom
        self.âge = âge

    #  renders class attributes in string form
    def identité(self: object) -> str:
        return f"[{self.prénom},{self.nom},{self.âge}]"


#  ---------------------------------- main
#  a Person object
p = Personne("Paul", "de la Hûche", 48)
#  method call
print(f"personne={p.identité()}")

  • 第 4 行:类的构造函数名为 __init__。与其他方法一样,其第一个参数是 self;
  • 第 20 行:使用类构造函数创建了一个 Person 对象;
  • 第 13–15 行:[identity] 方法返回一个字符串,表示该对象的内容;
  • 第 22 行:显示该人的身份;

结果


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_03.py
personne=[Paul,de la Hûche,48]
 
Process finished with exit code 0

12.4. 脚本 [classes_04]:静态方法

我们在 [modules] 文件夹中定义了以下 [Utils] 类(utils.py):

Image

class Utils:
    #  static method
    @staticmethod
    def is_string_ok(string: str) -> bool:
        #  string is a string
        erreur = not isinstance(string, str)
        if not erreur:
            #  is the chain empty?
            erreur = string.strip() == ''
        #  result
        return not erreur

注释

  • 第 3 行:[@staticmethod] 注解表明,带有该注解的方法是类方法,而非实例方法。这从注解方法的第一个参数不是 [self] 关键字这一事实中可以明显看出。因此,静态方法无法访问对象的属性。不要这样写:
u=Utils()
print(u.is_string_ok("abcd")

我们写

print(Utils.is_string_ok("abcd")

由于我们在上面写了 [Utils.is_string_ok],因此 [is_string_ok] 方法被称为类方法(此处指 Utils 类)。要实现这一点,[Utils.is_string_ok] 方法必须使用 [@staticmethod] 关键字进行注解。

静态方法 [Utils.is_string_ok] 允许我们在此处验证一段数据是否为非空字符串。

[classes_04]脚本对[Utils]类的用法如下:

#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from utilitaires import Utils

print(Utils.is_string_ok("  "))
print(Utils.is_string_ok(47))
print(Utils.is_string_ok(" q "))
  • 第 1-4 行:我们使用一个配置脚本;

配置脚本 [config.py] 如下:

def configure():
    import os

    #  configuration file folder
    script_dir = os.path.dirname(os.path.abspath(__file__))

    #  absolute paths of folders to put in the syspath
    absolute_dependencies = [
        f"{script_dir}/shared",
        "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/venv/lib/site-packages",
        "C:/myprograms/Python38/lib",
        "C:/myprograms/Python38/DLLs"
    ]

    #  update syspath
    from myutils import set_syspath
    set_syspath(absolute_dependencies)

    #  return the config
    return {}
  • 第 9 行:[shared] 文件夹将被添加到 Python 路径中;

执行结果如下:

1
2
3
4
5
6
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_04.py
False
False
True

Process finished with exit code 0

12.5. 脚本 [classes_05]:属性有效性检查

[classes_05] 脚本介绍了新概念:

  • 自定义异常类型的定义;
  • 定义 [_str_] 方法,该方法是类的默认身份识别方法;
  • 属性定义;
#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from utilitaires import Utils


#  a proprietary exception class derived from [BaseException]
class MyException(BaseException):
    #  do nothing: empty classroom
    pass


#  person class
class Personne:
    #  manufacturer
    def __init__(self: object, prénom: str = "x", nom: str = "y", âge: int = 0):
        #  first name: person's first name
        #  name: person's name
        #  age: age of the person

        #  parameter storage
        #  initializations will be made via setters
        self.prénom = prénom
        self.nom = nom
        self.âge = âge

    #  method toString of class
    def __str__(self: object) -> str:
        return f"[{self.__prénom},{self.__nom},{self.__âge}]"

    #  getters
    @property
    def prénom(self) -> str:
        return self.__prénom

    @property
    def nom(self) -> str:
        return self.__nom

    @property
    def âge(self) -> int:
        return self.__âge

    #  setters
    @prénom.setter
    def prénom(self, prénom: str):
        #  first name must be non-empty
        if Utils.is_string_ok(prénom):
            self.__prénom = prénom.strip()
        else:
            raise MyException("Le prénom doit être une chaîne de caractères non vide")

    @nom.setter
    def nom(self, nom: str):
        #  first name must be non-empty
        if Utils.is_string_ok(nom):
            self.__nom = nom.strip()
        else:
            raise MyException("Le nom doit être une chaîne de caractères non vide")

    @âge.setter
    def âge(self, âge: int):
        #  age must be an integer >=0
        erreur = False
        if isinstance(âge, int):
            if âge >= 0:
                self.__âge = âge
            else:
                erreur = True
        else:
            erreur = True
        #  mistake?
        if erreur:
            raise MyException("L'âge doit être un entier >=0")


#  ---------------------------------- main
#  a Person object
try:
    #  instantiation Person class
    p = Personne("Paul", "de la Hûche", 48)
    #  object display p
    print(f"personne={p}")
except MyException as erreur:
    #  error display
    print(erreur)

#  another object Nobody
try:
    #  instantiation Person class
    p = Personne("xx", "yy", "zz")
    #  object display p
    print(f"personne={p}")
except MyException as erreur:
    #  error display
    print(erreur)

#  another person without parameters this time
try:
    #  instantiation Person class
    p = Personne()
    #  object display p
    print(f"personne={p}")
except MyException as erreur:
    #  error msg display
    print(erreur)

#  you cannot access the __attr private attributes of the
p.__prénom = "Gaëlle"
print(f"p.prénom={p.prénom}")
print(f"p.__prénom={p.__prénom}")
p.prénom = "Sébastien"
print(f"p.prénom={p.prénom}")
print(f"p.__prénom={p.__prénom}")

  • 第 10–13 行:一个从 BaseException 类派生的 MyException 类(我们稍后会详细讨论这一点)。它并未为后者增添任何功能,仅用于提供自定义异常;
  • 第 19 行:构造函数的参数具有默认值。因此,操作 p = Person() 等同于 p = Person("x", "y", 0);
  • 第 34–45 行:类属性。这些是标注了 [@property] 关键字的方法,用于设置属性的值;
  • 第 47–77 行:类的设置器。这些是标注了 [@attributsetter] 关键字的方法。它们用于设置属性的值;
  • 第 48–54 行:[first_name] 属性的设置器。每当向 [first_name] 属性赋值时,都会调用此方法:
p=Personne(…)
p.prénom=valeur

第 2 行将触发 [p.firstName(value)] 的调用。使用设置器为属性赋值的优势在于,由于设置器是一个函数,我们可以验证赋予该属性的值是否有效;

  • 第 51 行:我们验证分配给 [first_name] 属性的值是否为非空字符串。为此,我们使用了前面提到的静态方法 [Utils.isStringOk]
  • 第 52 行:将 [first_name] 属性的值去除首尾空格后,赋值给 [self.__first_name] 属性。因此,此处并未直接使用 [first_name] 属性本身。 我们别无选择,否则将导致无限递归调用。我们本可以使用任何属性名称。但我们使用了以两个下划线开头的 [__prénom] 属性,这具有特殊含义:以两个下划线开头的属性属于类私有属性。这意味着它们在类外部不可见。因此,我们不能这样写:
p=Personne(…)
p.__prénom=valeur

实际上,我们很快就会看到,虽然可以这样写,但它并不会更改名字。它做了别的事情;

  • 第 53–54 行:如果赋给 first_name 的值不正确,将抛出异常。这样,调用代码就会知道其调用是错误的;
  • 第 35–37 行:[first_name] 属性。每当表达式中写入 [p.first_name] 时,它都会被调用。随后将调用 [p.first_name()] 方法。 第 37 行:我们返回 [__first_name] 属性的值,因为我们看到 [first_name] 属性的设置器会将其值赋给私有属性 [__first_name]
  • 第 56–62 行:[last_name] 属性的设置器与 [first_name] 属性的设置器构建方式类似。第 64–77 行中 [age] 属性的设置器也是如此;
  • 尽管 [first_name, last_name, value] 并非实际的属性(实际属性应为 [__first_name, __last_name, __age]),但我们将继续将其称为类属性,因为它们在实际使用中就是作为类属性存在的;
  • 第 19–28 行:类构造函数隐式地使用了 [first_name、last_name、age] 属性的设置器。实际上,通过在第 26 行编写 [self.first_name = first_name],隐式地调用了方法 [first_name(self, first_name)]。 随后将对 [first_name] 参数的有效性进行检查。其他两个属性 [last_name, age] 也是如此;
  • 采用这种模式,我们无法为类属性 [first_name, last_name, age] 赋值错误的值;
  • 第 30–32 行:__str__ 函数取代了之前名为 identity 的方法。名称 [__str__](前后各两个下划线)并非毫无意义。我们稍后将看到这一点;
  • 第 83–86 行:实例化一个 Person 对象,随后显示其身份信息;
  • 第 84 行:实例化;
  • 第 86 行:显示。该操作需要将对象 p 作为字符串显示。如果该方法存在,Python 解释器会自动调用 p.__str__()。该方法的作用与 Java 或 .NET 语言中的 toString() 方法相同;
  • 第 87–89 行:处理可能抛出的 MyException,随后显示错误信息;
  • 第 91–99 行:与上述操作相同,但针对使用错误参数实例化的第二个对象;
  • 第 102–109 行:与上述情况相同,但针对使用默认参数实例化的第三个人:未传递任何参数。此处将使用构造函数中这些参数的默认值;
  • 第 112–117 行:我们提到 [__first_name] 属性是私有的,因此通常无法从类外部访问。我们需要验证这一点;
  • 第 112–114 行:我们为 [__first_name] 属性赋值,然后检查 [__first_name] [first_name] 属性的值,这两者通常应保持一致;
  • 第 115–117 行:我们重复该操作,这次初始化 [first_name] 属性;

结果


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_05.py
personne=[Paul,de la Hûche,48]
L'âge doit être un entier >=0
personne=[x,y,0]
p.prénom=x
p.__prénom=Gaëlle
p.prénom=Sébastien
p.__prénom=Gaëlle
 
Process finished with exit code 0

备注

  • 第 5-6 行:我们可以看到,赋值语句 [p.__first_name = "Gaëlle"] 并未改变 [first_name] 属性的值(第 5 行);
  • 第 7-8 行:我们可以看到,赋值 [p.first_name = "Sébastien"] 并未改变 [__first_name] 属性的值(第 8 行);

我们可以得出什么结论?这很可能说明,操作 [p.__first_name = "Gaëlle"] 为该类创建了一个公共属性 [__first_name],但这与类内部操作的私有属性 [__first_name] 不同;

12.6. 脚本 [classes_06]:添加对象初始化方法

脚本 [classes_06][Person] 类添加了一个方法:

#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from utilitaires import Utils


#  a proprietary exception class derived from [BaseException]
class MyException(BaseException):
    #  do nothing: empty classroom
    pass


#  person class
class Personne:
    #  manufacturer
    def __init__(self: object, prénom: str = "x", nom: str = "y", âge: int = 0):
        #  first name: person's first name
        #  name: person's name
        #  age: age of the person

        #  parameter storage
        #  initializations will be made via setters
        self.prénom = prénom
        self.nom = nom
        self.âge = âge

    #  other initialization method
    def init_with_personne(self: object, p: object):
        #  initializes the current object with a person p
        self.__init__(p.prénom, p.nom, p.âge)

    #  method toString of class
    def __str__(self: object) -> str:
        return f"[{self.__prénom},{self.__nom},{self.__âge}]"

    #  getters
    

    #  setters
    


#  ---------------------------------- main
#  a Person object
try:
    #  instantiation Person class
    p = Personne("Paul", "de la Hûche", 48)
    #  object display p
    print(f"personne={p}")
except MyException as erreur:
    #  error msg display
    print(erreur)

#  another object Nobody
try:
    #  instantiation Person class
    p = Personne("xx", "yy", "zz")
    #  object display p
    print(f"p={p}")
except MyException as erreur:
    #  error msg display
    print(erreur)

#  another person without parameters this time
try:
    #  instantiation Person class
    p = Personne()
    #  object display p
    print(f"p={p}")
except MyException as erreur:
    #  error msg display
    print(erreur)

#  another Person obtained by recopying
try:
    #  instantiation Person class
    p2 = Personne()
    p2.init_with_personne(p)
    #  object display p2
    print(f"p2={p2}")
except MyException as erreur:
    #  error msg display
    print(erreur)

  • 与前一个脚本的区别在于第30至33行。我们添加了 initWithPerson 方法该方法调用了 __init__ 构造函数。与类型化语言不同,这里无法通过参数性质或返回值来区分同名方法。因此,无法拥有多个构造函数来根据不同的参数(本例中为 Person 类型的对象)创建对象;

结果


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_06.py
personne=[Paul,de la Hûche,48]
L'âge doit être un entier >=0
p=[x,y,0]
p2=[x,y,0]
 
Process finished with exit code 0

12.7. 脚本 [classes_07]:Person 对象的列表

现在我们将 [MyException][Person] 类放入一个模块中,这样我们就可以直接使用它们,而无需复制它们的代码:

Image

这两个类都将位于上文提到的 [myclasses.py] 模块中。

[classes_07] 脚本展示了我们可以创建一个对象列表:

#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from myclasses import Personne

#  ---------------------------------- main
#  create a list of person objects
groupe = [Personne("Paul", "Langevin", 48), Personne("Sylvie", "Lefur", 70)]
#  identity of these persons
for i in range(len(groupe)):
    print(f"groupe[{i}]={groupe[i]}")

  • 第 7 行:我们导入了 [Person] 类;
  • 第 11 行:一个 [Person] 类型的对象列表;
  • 第 13–14 行:遍历该列表以显示其中的每个元素;
  • 第 14 行:[print] 函数将显示表示对象 [group[i]] 的字符串。默认情况下,将调用这些对象的 [__str__] 方法;

结果


C:\Users\serge\.virtualenvs\cours-python-v02\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/v-02/classes/01/classes_07.py
groupe[0]=[Paul,Langevin,48]
groupe[1]=[Sylvie,Lefur,70]
 
Process finished with exit code 0

12.8. 脚本 [classes_08]:创建一个从 Person 类派生的类

我们在 [myclasses] 模块中定义了以下 [Teacher] 类:


# classe Enseignant
class Enseignant(Personne):
    # constructeur
    def __init__(self, prénom: str = "x", nom: str = "x", âge: int = 0, discipline: str = "x"):
        # prénom : prénom de la personne
        # nom : nom de la personne
        # âge : âge de la personne
        # discipline : discpline enseignée
 
        # initialisation du parent
        Personne.__init__(self, prénom, nom, âge)
        # autres initialisations
        self.discipline = discipline
 
    # toString
    def __str__(self) -> str:
        return f"enseignant[{super().__str__()},{self.discipline}]"
 
    # propriétés
    @property
    def discipline(self) -> str:
        return self.__discipline
 
    @discipline.setter
    def discipline(self, discipline: str):
        # la discipline doit être une chaîne non vide
        if Utils.is_string_ok(discipline):
            self.__discipline = discipline
        else:
            raise MyException("La discipline doit être une chaîne de caractères non vide")
  • 第 2 行:声明 Teacher 类为 Person 类的子类。子类拥有父类的所有属性(包括属性与方法)以及自身的属性;
  • 第 13 行:[Teacher] 类定义了一个新属性 [subject]
  • 第 11 行:派生类 Teacher 的构造函数必须调用父类 Person 的构造函数,并向其传递预期的参数;
  • 第 17 行:[super()] 函数调用父类。此处,我们调用了父类的 [__str__] 函数;
  • 第 19–30 行:定义了新属性 [subject] 的 getter 和 setter 方法;

[classes_08]脚本使用[Teacher]类的示例如下:

#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from myclasses import Personne, Enseignant

#  ---------------------------------- main
#  creation of an array of Personne and derived objects
groupe = [Enseignant("Paul", "Langevin", 48, "anglais"), Personne("Sylvie", "Lefur", 70)]
#  identity of these persons
for i in range(len(groupe)):
    print(f"groupe[{i}]={groupe[i]}")

  • 第 7 行:我们导入了 [myclasses.py] 文件中定义的 [Person] [Teacher] 类;
  • 第11–14行:我们定义了一组人,然后显示他们的身份;

结果


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_08.py
groupe[0]=enseignant[[Paul,Langevin,48],anglais]
groupe[1]=[Sylvie,Lefur,70]
 
Process finished with exit code 0

12.9. 脚本 [classes_09]:从 Person 类派生的第二个类

脚本 [classes_09] 介绍了从 [Person] 类派生的 [Student] 类。该类在模块 [myclasses] 中定义如下:


# classe Etudiant
class Etudiant(Personne):
    # constructeur
    def __init__(self: object, prénom: str = "x", nom: str = "y", âge: int = 0, formation: str = "x"):
        Personne.__init__(self, prénom, nom, âge)
        self.formation = formation
 
    # toString
    def __str__(self: object) -> str:
        return f"étudiant[{super().__str__()},{self.formation}]"
 
    # propriétés
    @property
    def formation(self) -> str:
        return self.__formation
 
    @formation.setter
    def formation(self, formation: str):
        # la formation doit être une chaîne non vide
        if Utils.is_string_ok(formation):
            self.__formation = formation
        else:
            raise MyException("La formation doit être une chaîne de caractères non vide")

[classes_09] 脚本如下所示使用 [Student] 类:

#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from myclasses import Personne, Enseignant, Etudiant

#  ---------------------------------- main
#  creation of an array of Personne and derived objects
groupe = [Enseignant("Paul", "Langevin", 48, "anglais"), Personne("Sylvie", "Lefur", 70),
          Etudiant("Steve", "Boer", 22, "iup2 qualité")]
#  identity of these persons
for personne in groupe:
    #  person display
    print(personne)

  • 此脚本与前一个类似。

结果


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_09.py
enseignant[[Paul,Langevin,48],anglais]
[Sylvie,Lefur,70]
étudiant[[Steve,Boer,22],iup2 qualité]
 
Process finished with exit code 0

12.10. 脚本 [classes_10]:[__dict__] 属性

[classes_10] 脚本介绍了 [__dict__] 属性,我们后续会频繁使用它:

#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from myclasses import Etudiant

#  ---------------------------------- main
#  student creation
étudiant=Etudiant("Steve", "Boer", 22, "iup2 qualité")
#  property dictionary
print(étudiant.__dict__)

注释

  • 第 1-4 行:应用程序的初始化;
  • 第 7 行:导入 [Student] 类;
  • 第 11 行:创建一个学生实例;
  • 第 13 行:使用预定义方法 [__dict__](标识符前后各有一个下划线);

结果如下:

1
2
3
4
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_10.py
{'_Personne__prénom': 'Steve', '_Personne__nom': 'Boer', '_Personne__âge': 22, '_Etudiant__formation': 'iup2 qualité'}

Process finished with exit code 0
  • 在第 2 行,我们得到一个字典,其键是对象的属性,并在属性名前加上所属类的名称作为前缀。我们将利用这个字典在对象与字典之间建立一座桥梁;