美文网首页
关于类的继承和静态属性

关于类的继承和静态属性

作者: 醉看红尘这场梦 | 来源:发表于2020-03-14 10:39 被阅读0次

在这一节,结合之前学过的内容,我们来看如何在Python中表示类之间的继承关系。

首先,基于上一节的例子,我们创建了三个文件:

  • Person.py
  • Employee.py
  • Main.py

现在,我们打开Employee.py,在其中给Person添加一个派生类Empolyee

from Person import Person

class Employee(Person):

在这里例子里,有三点值得我们注意:

第一、为了在Employee.py中使用Person,我们在文件开始使用了from Person import Person。由于Person定义在Person.py中,因此from后面的模块名是Person,从中,我们引入了class Person。虽然它们同名,大家要把它们分清楚。

第二、我们的类定义,要和import语句保持两个空行,这是Python中推荐的编码习惯。

第三、Python中,我们用(Person)这样的形式表示基类。在这个括号里,我们可以用逗号分隔多个基类。但经验告诉我们,多重继承在更多时候并不会像我们想象一样正常工作。因此,我们还是暂时只讨论单一继承的情况。

派生类的init方法

定义了派生类之后,第一件事,就是定义它的__init__方法,就像你已有的OO经验一样,在派生类的__init__方法里,我们要初始化派生类和基类两部分内容:

class Employee(Person):
    def __init__(self, work_id, name, age):
        Person.__init__(self, name, age)
        self.work_id = work_id

这里唯一要注意的是派生类调用基类__init__的方法,是通过类名调用的。然后,我们就可以这样来创建派生类对象了。在Main.py中,添加下面的代码:

from Employee import Employee

mars = Employee(11, 'mars', 30)

既然有了派生类,继续之前,我们介绍几个判断类关系的函数。要判断某个类是否是另外一个类的派生类可以使用issubclass

print(issubclass(Employee, Person))  # True

它的第一个参数是派生类类型,第二个参数是基类类型,最后,返回一个boolean值。要判断一个对象是否是某个类的对象,可以使用isinstance

print(isinstance(mars, Person))    # True
print(isinstance(mars, Employee))  # True

它的第一个参数是类对象,第二个参数是要判断所属的类型,同样返回一个boolean值。要注意的是,这两个函数都直接传给它们类型就好了,并不用给它们传递类型的字符串名称。

重写类方法

在Python里,重写基类方法和在派生类中定义方法是没有任何区别的,我们不用像Swift一样使用override关键字,只要重定义了,就算是重写了。

我们通过重写几个Python class中内置的方法,来理解这个过程。实际上,之前我们已经干过这个事了,就是在派生类中重写__init__。除此之外,class中还有以下可以重写的方法。

理解reprstr

每个Python class都包含了两个和类型的字符串表达方式有关的方法,叫做__repr____str__,默认情况下,它们返回的结果是相同的。在Main.py中,添加下面的代码:

print(mars.__repr__())
print(mars.__str__())

# <Employee.Employee object at 0x109563550>

执行一下就会看到,它们返回的都是类似上面注释中的结果。那么,为什么要有两个方法呢?也许你可以找到很多关于这个问题的讨论。但在我看来,理解下面这两点就足以让你用对它们了:

  • 首先,__str__是通过调用__repr__实现的,因此,它的默认实现确没什么特别的用途;
  • 其次,__repr__是给一个类型的开发者使用的,它应该包含关于类型的更多信息;而__str__的描述则是针对类型的使用者使用的,它应该尽可能易读、友好;

有了上面这两个原则之后,我们就可以试着改写__str__的实现,让它只返回类型名称本身,例如这样:

class Employee(Person):
    # ...

    def __str__(self):
        return "Employee"

稍后,我们还会看到更好的获取类型名称的方法,这里我们只是直接返回了Employee。重新执行下,就会发现,此时__repr____str__的结果不同了。

'''
<Employee.Employee object at 0x10e388518>
Employee
'''

重定义对象的比较行为

除了重定义类型的表达方式之外,我们还可以明确指定两个类对象的比较方法。在Python 2的时候,这是通过重写__cmp__方法实现的。但在Python 3中,已经不提倡这么做了。Python 3中提供了一组表意更明确的函数:

  • __eq__: equal
  • __ne__: not equal
  • __lt__: less than
  • __le__: less equal
  • __gt__: greater than
  • __ge__: greater equal

首先来看,当我们不定义这些方法的时候,比较两个Employee对象,执行的是比较对象引用的操作:

mars = Employee(11, 'mars', 30)
eleven = Employee(11, 'mars', 30)

print(mars == eleven)  # false

显然,marseleven引用的是不同的对象,因此这个比较结果肯定是False。这时,如果我们希望按照对象的值比较,认为只要姓名、年龄和工号相等,那么两个Employee对象就相等,就可以重写__eq__方法:

class Employee(Person):
    # ...
    def __eq__(self, other):
        return self.name == other.name and \
               self.age == other.age and \
               self.work_id == other.work_id

可以看到,__eq__有两个参数,self可以理解为==的左操作数,other==的右操作数。而它的比较逻辑,就是逐个比较Employee的每个属性,很简单。

定义好__eq__之后,之前的比较结果,就变成True了。

定义Class attributes

在这一节最后,我们来看如何给class添加静态属性,在Python里,这叫做class attribute

之前我们提到过,所有直接定义在__init__方法里的属性,都是类对象属性,它们都是绑定在某个对象上的。如果我们把属性定义在__init__外面,这个属性就被所有的类对象共享了。例如,我们添加一个统计所有员工对象数量的counter

class Employee(Person):
    counter = 0

可以看到,counter的初始化是在定义的时候完成的。这样,counter就会被所有的类对象共享了,我们可以在每次__init__方法被调用的时候,把它加1:

def __init__(self, work_id, name, age):
    Person.__init__(self, name, age)
    self.work_id = work_id
    Employee.counter += 1

此时,由于我们有marseleven两个Employee对象,访问counter的时候,得到的值,就是2了:

mars = Employee(11, 'mars', 30)
eleven = Employee(11, 'mars', 30)

print(Employee.counter)  # 2

在上面的例子里可以看到看到,访问class attribute的时候,我们要使用ClassName.Attribute这样的形式。

看到这,你可能会想,这个counter可以随便被人修改啊,用它来计数并不靠谱。没错,默认情况下,class的所有的属性和方法都是可以被外部访问的。如果我们要隐藏这个属性,可以在它前面使用两个下划线:

class Employee(Person):
    __counter = 0

    def __init__(self, work_id, name, age):
        Person.__init__(self, name, age)
        self.work_id = work_id
        Employee.__counter += 1

然后,在Main.py里,无论你用哪种方式访问counter

print(Employee.counter)
print(Employee.__counter)

都会触发AttributeError异常,并提示我们对应的counter属性不存在。

不过,你也别太当真,因为这至多也就是一层设计意图上的保障罢了,它只能提醒你自己,__counter只能用在Employee内部。

因为,Python中有一个魔术前缀,就是一个下划线加上类名,对于我们的例子来说,就是_Employee,通过它,你就可以访问到私有成员了。于是,在Main.py里,我们把代码改成这样:

print(Employee._Employee__counter)  # 2

就可以恢复执行了,而且,读写均可 :-)

相关文章

  • ES6解读3:类class

    类的继承 类的getter和setter方法 静态方法以及静态属性 注意:静态方法只能是类调用,不能实例调用

  • ES的类与继承

    ES5中的类与继承 构造函数继承,原型继承,组合式继承 静态方法,静态属性,实例方法,实例属性 ES6中的类与继承...

  • ECMAScript6--14.类与对象

    1.类 类的概念基本语法类的继承静态方法静态属性gettersetter 2.类的基本定义和生成实例 3.继承:e...

  • 关于类的继承和静态属性

    在这一节,结合之前学过的内容,我们来看如何在Python中表示类之间的继承关系。 首先,基于上一节的例子,我们创建...

  • Python面向对象编程

    类相关知识 对象相关知识 类属性增删改查 实例属性增删改查 对象与实例属性 静态属性 类方法 静态方法 组合 继承...

  • python基础之继承特性

    1、什么是继承 继承就是让类和类之间产生父子关系,子类可以拥有父类的静态属性和方法。[继承就是可以获取另外一个类中...

  • java基础知识问答

    一:父类静态方法或属性可以被继承重写吗? 静态方法或属性,一旦创建,就和这个类的对象脱离了关系,无所谓继承。如果子...

  • ECMAScript6 class

    继承 静态属性和方法

  • python如何实现类似java的静态变量

    静态变量可以使得一个类的多个实例共享属性,也包括继承关系类的共享属性 而python没有静态变量,只有静态方法(即...

  • 谈谈 class

    创建类的写法 静态属性与方法 静态属性与静态方法只能通过类来访问,不可以通过实例来进行访问。但是子类可以继承到,继...

网友评论

      本文标题:关于类的继承和静态属性

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