美文网首页python
面向对象的Python:类(classes)和对象object(

面向对象的Python:类(classes)和对象object(

作者: Python_Camp | 来源:发表于2022-10-15 21:10 被阅读0次

    面向对象的Python:类class(es)和对象object(s)

    面向对象的编程是当今最广泛使用的编程范式,几乎所有的编程范式都提供了一种创建和管理对象的方法。下面是对象的含义。

    面向对象编程中的对象的表示方法

    大多数编程语言都提供了一个叫做 "类 "的关键字来创建一个对象,python也不例外。

    那么,什么是类?

    一个类定义了蓝图,它可以被实例化来创建对象(s)

    一个类定义了可识别的特征和行为,对象将在此基础上被识别。关键字class在Python中也有同样的作用。

    然而,在我们深入了解类和对象之前,让我们先来谈谈Python语言的另一个内置数据结构--字典。

    Python 字典
    字典是Python的内置数据结构,它用 "键 "和 "值 "对来实现 "哈希 "函数。

    哈希函数在键上工作,生成哈希值,并根据这些哈希值存储相应的值。存储取决于哈希函数,这就是为什么字典有时不按我们创建的顺序存储的原因。

    字典也可以代表一个对象的静态状态,即不改变其行为的对象,其特点是提到的键(名称)和值对。

    作为一个例子,让我们考虑当地社区中人们的邮政地址,我们可以将单个地址表示为

    p1 = {'name' : "ABC" , 'street': "中央大街-1" }
    p2 = {'name' : "DEF" , 'street': "中央街-2" }
    p3 = {'name' : "AXY" , 'street': "中央街-1" }
    

    这里p1, p2 & p3是住在同一条街上的不同人,这些字典描述了对象(即本例中的人)的静态可识别特征。

    我们也可以使用Python类来存储对象的静态状态,让我们看看如何做到这一点。

    Python类
    与其他OOP语言类似,Python中的类提供了一个创建对象的蓝图。就像我们在上面用字典存储邮政地址一样,我们也可以用类的对象来存储它们。

    我们可以先创建一个没有预定义蓝图的假类PostalAddress。Python允许为对象添加运行时成员(Identifiable Characteristics),我们将使用同样的方法来创建居住在同一地区的人的地址,类似于上面的字典p1和p2。

    class PostalAddress:
        pass
    
    cP1 = PostalAddress()
    # 为ABC这个人创建一个实例
    cP1.name = "ABC"
    cP1.street = "Central Street - 1"
    
    # 为DEF创建一个实例
    cP2 = PostalAddress()
    cP2.name = "DEF"
    cP2.street = "Central Street - 2"
    

    所以,我们现在有两种方法来创建和保存PostAddress,即通过字典或使用类的对象。顺便说一下,对象也可以用一个内置的函数__dict__来表示。

    print(cP1.__dict__)
    print(cP2.__dict__)
    # 输出将与p1和p2相同
    {'name': 'ABC', 'street': 'Central Street - 1'}
    {'name': 'DEF', 'street': 'Central Street - 2'}
    

    这并不意味着 Python 将对象存储为字典,但我们可以有把握地得出结论,字典也可以表示静态状态的对象。一旦我们使用 __dict__得到了字典,我们就可以用键来访问元素,就像我们对普通字典一样。

    print(p1["name"]) 
    # 从普通字典中打印出 ABC
    
    print(cP1.__dict__["name"]) 
    # 从对象中打印 ABC
    

    根据我们目前所了解的情况,我们可能会形成一种观点,即至少在上述情况下,我们使用字典还是对象并不重要。

    好吧,对于创建非常简单的静态对象来说,这可能是真的,但是类不仅提供了添加行为的能力(从而允许改变对象的状态),而且还提供了额外的功能,帮助程序员以简单和可维护的方式管理对象。

    在我们研究类所提供的功能之前,让我们先看看我们上面写的代码的一个主要缺点。我们可以把上面写的代码改成

    p1 = {'name' : "ABC" , 'street': "Central Street - 1" }
    p2 = {'names' : "DEF" , 'street': "Central Street - 2", "NewVar" : "Not Needed" }
    # and
    class PostalAddress:
        pass
    
    cP1 = PostalAddress()
    cP1.name = "ABC"
    
    cP2 = PostalAddress()
    cP2.names = "DEF"
    cP2.NewVar = "Not Needed"
    

    如果你没有注意到,键 "name "被错写成 "names",并且在 "p2 "和 "cP2 "中分别创建了额外的 "NewVar"。然而,两者都是有效的字典和对象。

    这就是我们使用类来定义蓝图的地方,这样每个相同类型的对象都有相同的特征。

    Python 提供了一个特殊的成员函数叫做 __init__,每次我们创建一个类的实例时都会调用这个函数,这个函数被用来创建对象的蓝图。

    有时 __init__也被称为构造函数。

    __init__(...)函数
    Python 为对象提供了特殊的函数,其前缀和后缀是"__"。

    我们先前使用的函数 __dict__也是一个类似的特殊函数。

    当一个对象被创建时,函数 __init__被调用。这个函数需要一个强制性的参数,称为self,这里self指的是对象本身。

    class PostalAddress:
        def __init__(self):
            pass
    

    在这个

    __init__ 
    

    函数中,我们创建了局部实例变量,这些变量定义了由这个类构建的对象的状态(可识别的特征)。

    class PostalAddress:
      def __init__(self):
        self.name = "ABC"
        self.street = "Central Street - 1"
    
    cP1 = PostalAddress()
    print(cP1.__dict__)
    

    self在Python中不是一个关键字,它只是一个规范,用self这个词作为对自己的引用,然而,我们可以为它命名任何我们想要的东西,比如说

    
    class PostalAddress。
      def __init__(theClassInstance):
          theClassInstance.name = "ABC"
          theClassInstance.street = "Centeral Street - 1"
    

    然而,self的用法是如此的普遍,以至于它已经成为类中方法的一个事实上的关键词,很难找到一个程序员除了使用self之外还使用其他的东西。在进一步的例子中,我也将使用self来描述类的实例,即对象。

    向类中添加函数

    类中的函数提供了对象的行为方面。让我创建一个函数来打印对象的当前状态。这个函数将被称为prnInfo,看起来像

    class PostalAddress:
        def __init__(self):
            self.name = "ABC"
            self.street = "Centeral Street - 1"
        def prnInfo(self):
            print("Name =>", self.name, " Street =>", self.street)
    
    cP1 = PostalAddress()
    cP1.prnInfo()
    

    就像 __init__ 函数一样,所有的函数都把self作为一个强制参数。然而,当我们调用函数时,我们不需要向它传递任何参数,因为 python 自动将对象实例作为self

    我们也可以使用类而不是对象来调用函数,然而,如果我们想通过类来调用方法,我们需要明确地传递实例,即 self,因为当我们通过类来调用实例时,Python 不知道该把哪个实例作为 self。

    使用类的调用应该看起来像

    
    cP1 = PostalAddress()
    # 使用类来调用函数,我们提供实例
    PostalAddress.prnInfo(cP1)
    # 这与
    cP1.prnInfo()
    

    参数化的__init__
    PostalAddress这个类类似于一个静态字典,因为我们已经硬编码了名称和街道。然而,我们想用不同的值来创建不同的实例对象。

    为了达到同样的目的,在__init__函数中把这些值作为输入参数,正如下面的代码所描述的那样

    # PostalAddress将名字和街道作为输入参数
    class PostalAddress:
        def __init__(self, name, street):
            self.name = name
            self.street = street
        def prnInfo(self):
            print("Name =>", self.name, " Street =>", self.street)
    
    cP1 = PostalAddress("ABC", "Central Street - 1")
    cP1.prnInfo()
    cP2 = PostalAddress("DEF", "Central Street - 2")
    cP2.prnInfo()
    

    __init__的多种变化

    不像其他面向对象的编程语言,如 "Java "和 "C++",Python 没有提供用不同参数集重载

    __init__ 
    

    的机制。任何后来的函数定义都会覆盖之前的函数。

    避免这种情况的一个方法是为 __init__ 函数提供默认参数,这样我们就可以使用变量参数创建实例。

    class PostalAddress。
      def __init__(self, name = "Default Name", street = "Central Street - 1")。
        self.name = name
        self.street = street
    
    cP0 = PostalAddress(); 
    # 0参数
    cP1 = PostalAddress("ABC") 
    # 1个参数
    cP2 = PostalAddress("DEF", "Central Street - 2") 
    # 2参数
    

    在多个函数中创建实例变量

    并不是说我们只能在__init__函数中创建实例变量。我们可以在类的任何成员函数中做同样的事情。

    唯一的问题是,这些实例变量只有在创建实例变量的函数被调用时才会被创建。

    
    class PostalAddress:
      def __init__(self, name = "Default Name", street = "Central Street - 1"):
        self.name = name
        self.street = street
      
     def createMember(self):
        self.newMember = "Temporary Value"
    
    cP0 = PostalAddress();
    print(cP0.__dict__);
    # prints {'name': 'Default Name', 'street': 'Central Street - 1'}
    cP0.createMember();
    print(cP0.__dict__);
    # print {'name': 'Default Name', 'street': 'Central Street - 1', 'newMember': 'Temporary Value'}
    

    类变量

    到目前为止,我们一直以居住在一个社区的人为例来编写我们的代码。大多数时候,一个小社区里的人的地址都有一些共同的信息,比如说邮政编码。

    这意味着PostalAddress类的所有对象必须有相同的邮政编码。
    实现这个目的的一个方法是在init方法中硬编码邮政编码,这样它就可以在这个类中创建的每一个对象中使用。

    class PostalAddress:
        def __init__(self, name = "Default Name", street = "Central Street - 1"):
            self.name = name
            self.street = street
            self.postalcode = 12345 # 硬编码邮政编码
    
    cP0 = PostalAddress();
    print(cP0.__dict__); # 打印 {'postalcode': 12345, 'name': 'Default Name', ' street': 'Central Street - 1'}.
    cP1 = PostalAddress("ABC")
    print(cP1.__dict__); # prints {'postalcode': 12345, 'name': 'ABC', ' street': 'Central Street - 1'}
    

    然而,如果我们以后想改变邮政编码,这就带来了一个问题。唯一的办法是为每个对象实例手动改变,如

    cP0.postalcode = 54321
    cP1.postalcode = 54321
    ...
    ...
    cPn.postalcode = 54321
    

    这不仅是繁琐的,而且容易出错。幸运的是,在这些情况下,我们可以通过类变量来拯救我们的生活。

    类变量是在类中声明的变量,但不在类的任何方法中。

    这个变量对PostalAddress的所有实例都可用。让我们看看我们如何定义类变量

    class PostalAddress:
      postalCode = 12345; # class Variable
      def __init__(self, name = "Default Name", street = "Central Street - 1")。
          self.name = name
          self.street = street
    
    cP0 = PostalAddress()
    print(cP0.postalCode) # print 12345
    

    在研究如何改变类变量以使类的每一个实例都被更新为新的postalCode之前,我们需要了解类变量和实例变量之间的一个主要区别

    类变量被定义为类的一部分,而不是实例的一部分

    如果我们使用 print(cP0.__dict__)打印实例 cP0 的字典,我们不会在其中找到 'postalCode' 。

    print(cP0.__dict__)
    # printts {'street': 'Central Street - 1', 'name': ' Default Name'}
    

    为了找到'postalCode',我们需要打印该类的字典,可以打印为

    print(PostalAddress.__dict__)
    # 打印 {'postalCode': 12345. .... }以及其他许多东西
    

    这是 python 的查找序列,它首先查看 "实例",如果没有找到变量/函数,它就查看 "类"。

    这就是我们可以使用实例对象打印cP0.postalCode的原因。

    类的方法
    可以通过两种方式改变类的变量,要么使用类本身,要么使用类的函数。如果使用类本身来改变,我们需要写下以下代码

    PostalAddress.postalCode = 54321
    

    这不仅会改变现有实例的 "postalCode",也会改变这一行执行后创建的所有实例的 "postalCode"。

    
    # 实例创建前
    cP0 = PostalAddress()
    cP1 = PostalAddress()
    
    PostalAddress.postalCode = 54321
    # 改变后创建的实例
    cP2 = PostalAddress()
    
    print(cP0.postalCode) # 打印出54321
    print(cP1.postalCode) # 打印54321
    print(cP2.postalCode) #打印54321
    

    另一种实现的方法是使用Python中的类方法。类方法是一种特殊的方法,它以类为参数,而不是以实例即self为参数。

    在定义类方法的时候,我们需要使用关键字@classmethod明确地告诉Python这个方法是一个类方法。

    class PostalAddress:
      postalCode = 12345; # class Variable
      def __init__(self, name = "Default Name", street = "Central Street - 1"):
          self.name = name
          self.street = street
    
      @classmethod
      def newPostalCode(cls, newcode):
        cls.postalCode = newcode
    

    实例方法将self作为强制参数,而类方法将class作为强制参数。

    在这两种情况下,我们都不需要明确地传递Instance或Class,Python会自动处理它。

    现在我们可以通过Instance或者Class来调用这个新方法newPostalCode。在这两种情况下,最终的结果都是一样的。下面是我们如何使用实例调用这个方法的

    
    cP0 = PostalAddress()
    cP1 = PostalAddress()
    # 使用实例调用
    cP0.newPostalCode(9999)
    cP2 = PostalAddress()
    
    print(cP0.postalCode) # 打印9999
    print(cP1.postalCode) # 打印9999
    print(cP2.postalCode) # 打印 9999
    而我们可以使用类来调用
    
    cP0 = PostalAddress()
    cP1 = PostalAddress()
    # 使用实例调用
    PostalAddress.newPostalCode(9999)
    cP2 = PostalAddress()
    
    print(cP0.postalCode) # 打印9999
    print(cP1.postalCode) # 打印9999
    print(cP2.postalCode) #打印9999
    

    这就是关于面向对象的Python的基本介绍,应该足以让读者理解类和对象之间的区别,并允许他们编写基本的面向对象的Python代码。

    本文由mdnice多平台发布

    相关文章

      网友评论

        本文标题:面向对象的Python:类(classes)和对象object(

        本文链接:https://www.haomeiwen.com/subject/pvjkzrtx.html