前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python面向对象的实际例子(一)

Python面向对象的实际例子(一)

作者头像
zy010101
发布2022-01-10 09:41:03
8460
发布2022-01-10 09:41:03
举报
文章被收录于专栏:程序员

面向对象的实际例子

下面我们将构造两个类,分别是Person类和Student类。

  • Person类将创建一个处理和人相关的信息的类
  • Student类将定制化Person,修改了所继承的行为。 下面我们来一步步构造上面这两个类。

创建类并编写构造函数

代码语言:javascript
复制
class Person:
    def __init__(self, name, job, age):
        self.name = name
        self.job = job
        self.age = age

class语句定义了名为Person的类,然后在构造函数(__init__)中给self赋值。构造函数在实例化的时候会自动调用并自动将实例传入第一个参数self中,然后通过给self赋值,实例就拥有了name,job,age属性。

对于熟悉C++的人而言self.name=name是非常熟悉的,在C++里这里就是this->name=name赋值运算符左边的是实例的属性,而右边的是构造函数的形式参数。

现在,我们修改一下Person类的构造函数,给job和age添加上默认值,显得更加合理。

代码语言:javascript
复制
class Person:
    def __init__(self, name, job=None, age=1):
        self.name = name
        self.job = job
        self.age = age

现在,我们实例化Person的时候,可以之传入name即可。接下来实例化两个对象。

代码语言:javascript
复制
class Person:
    def __init__(self, name, job=None, age=1):
        self.name = name
        self.job = job
        self.age = age
    

if __name__ == "__main__":
    person1 = Person('Zhangsan', '法外狂徒', age=30)
    person2 = Person('Lisi')

    print(person1.name, person1.job)
    print(person2.name, person2.job)

执行这段代码的输出如下所示:

代码语言:javascript
复制
Zhangsan 法外狂徒
Lisi None   

上面的例子现在还是非常简单的,但是却展示了一些核心内容,person1和person2的属性是独立的,它们是两个不同的命名空间。我们可以有多个Person类的实例化,就像一段代码中可以有很多个列表一样。类是对象工厂

给类添加方法

随着时间推移,人的年龄会增加,现在给Person类加上一个addage方法用来增加年龄。

代码语言:javascript
复制
class Person:
    def __init__(self, name, job=None, age=1):
        self.name = name
        self.job = job
        self.age = age
    
    def addage(self, age=1):    # 默认age=1
        self.age += age


if __name__ == "__main__":
    person1 = Person('Zhangsan', '法外狂徒', age=30)
    person2 = Person('Lisi')

    print(person1.name, person1.job)
    print(person2.name, person2.job)

    person2.addage()        # 增加年龄
    print(person2.age)

实际上,我们完全可以在类外直接操作age属性来完成年龄的增加。例如:

代码语言:javascript
复制
person2.age += 1
print(person2.age)

类方法和在类外修改相比,提供了更好的可维护性。而且这样的方法将会是所有实例都拥有的方法。这就是“封装”带来的好处。

运算符重载

现在,为了更方便的显示打印,我们需要重载运算符来实现这点。先来看看我们现在直接打印person1对象的结果:

代码语言:javascript
复制
print(person1)

打印结果如下:

代码语言:javascript
复制
<__main__.Person object at 0x7f2f209fff40>

可以看到,打印了类名和在内存中的地址。不太直观,因此我们重载__repr__或者__str__来提供更好的显示效果。这里选择重载__repr__。如下所示:

代码语言:javascript
复制
class Person:
    def __init__(self, name, job=None, age=1):
        self.name = name
        self.job = job
        self.age = age
    
    def addage(self, age=1):
        self.age += age

    def __repr__(self):     # 重载__repr__
        return F"{self.name},{self.job},{self.age}"

现在,我们来执行打印person1的代码,输出如下所示:

代码语言:javascript
复制
Zhangsan,法外狂徒,30

关于__repr__,这里不做介绍,这里的重点是说明运算符重载的用处。

继承父类,定制行为

下面,我们来继承父类,实现定制化的行为,例如,我们需要一个学生类,那么学生的职业就是学生。我们的Student类实现如下所示:

代码语言:javascript
复制
class Student(Person):
    def __init__(self, name, age=3):
        Person.__init__(self, name, job='student', age=age)

Student继承Person类,然后覆盖了Person类的构造函数。覆盖的方式很巧妙,将job='student’传入给了父类Person的构造函数。

前面我们说过,我们很少使用X.__XXX__的方式去调用双下划线方法,但是这里我们使用了Person.__init__直接调用了父类的构造函数。需要注意当我们使用类.方法这种方式的时候,需要手动传入self参数。因为使用实例.方法调用的时候,python会自动将实例传入self参数。 现在来生成一个Student对象,然后打印一下看看输出。

代码语言:javascript
复制
student1 = Student('xiaoming', age=8)
print(student1)

输出结果如下所示:

代码语言:javascript
复制
xiaoming,student,8

扩展

通常而言,作为学生是会有一个成绩好坏的评价指标。我们现在给学生类加上评价方法以及评价指标属性。现在的Student类如下所示:

代码语言:javascript
复制
class Student(Person):
    def __init__(self, name, age=3, grade='E'):
        self.grade = grade      # 成绩属性
        Person.__init__(self, name, job='student', age=age)

    def setGrade(self, grade):  # 设置成绩
        self.grade = grade
    
    def __repr__(self):     # 覆盖父类的__repr__
        return Person.__repr__(self) + "," + self.grade

我们给学生扩展了一个属性grade用来表示学生的成绩情况,默认值为E,同时新增方法setGrade来设置学生的成绩。覆盖父类的__repr__方法来实现子类的__repr__

代码语言:javascript
复制
student1 = Student('xiaoming', age=8)
student1.setGrade('B')
print(student1)

输出结果如下:

代码语言:javascript
复制
xiaoming,student,8,B

组合类

组合类就是把对象嵌套在一起,来形成组合对象。我们来看一下新的类Manager

代码语言:javascript
复制
class Manager:
    def __init__(self, name, age, grade='E'):
        self.stu = Student(name, age, grade)
    
    def __getattr__(self, attr):
        return getattr(self.stu, attr)

    def __repr__(self):
        return str(self.stu)

stu1 = Manager('xiaoming', age=8)
stu1.setGrade('B')      # 通过__getattr__,我们能够使用setGrade方法。
print(stu1)

Manager类的属性stu是Student类的实例,输出和上面的student1是一模一样的。值得注意的是Manager是代理模式的一个典型代表。委托是一种基于组合的结构,它管理一个被包装在内部的对象。

需要介绍一下__getattr__,我们使用stu1.setGrade的时候,Manager类并没有setGrade方法。但是,我们调用成功了,这就得益于当访问object不存在的属性时会调用__getattr__方法。该方法在Manager的实现中是使用getattr() 函数返回Student对象的属性值。

既然__getattr__可以获取Student实例的属性,那么为什么还需要实现__repr__方法? 这是因为python2.2引入了新式类,我们在Python3中只有所谓的“新式类”,新式类中是无法通过通用属性管理器找到它们的隐式属性。因为必须得实现__repr__方法才能打印stu对象。

总结

到这里也差不该结束这个例子了,这个例子差不多说明了设计OOP的一些思路。虽然它不够健全,但是它确实说明了一些问题。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/12/31 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 面向对象的实际例子
    • 创建类并编写构造函数
      • 给类添加方法
        • 运算符重载
          • 继承父类,定制行为
            • 扩展
              • 组合类
                • 总结
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档