zoukankan      html  css  js  c++  java
  • Python面向对象编程

    对象魔法

    1.多态

    多态指的是能够同样地对待不同类型和类的对象,既无需知道对象属于哪个类就可调用其方法。

    2.封装

    对象可能隐藏(封装)其内部状态。在有些语言中,这意味着对象的状态(属性)只能通过其方法来访问。在Python中,所有的属性都是公有的,但直接访问对象的状态时程序员应谨慎行事,因为这可能在不经意间导致状态不一致。

    3.继承

    一个类可以是一个或多个类的子类,在这种情况下,子类将继承超类的所有方法。你可以指定多个超类,通过这样做可组合正交(独立且不相关)的功能。为此,一种常见的做法是使用一个核心超类以及一个或多个混合超类。

    每个对象都属于特定的类,并被称为该类的实例。

    例如,你在窗外看到一只鸟,这只鸟就是“鸟类”的一个实例。鸟类是一个非常通用的(抽象)的类,它有多个子类:你看到的那只鸟可能属于子类“云雀”。你可以将“鸟类”视为由所有鸟组成的集合,而“云雀”是其一个资料集,一个类的对象为另一个类的对象的子集时,前者就是后者的子类,因此“云雀”为“鸟类”的子集,而“鸟类”为“云雀”的超类。

    1.创建自定义类

     1 class Student(object):
     2 
     3     # 构造方法(构造器、构造子 - constructor)
     4     # 调用该方法的时候不是直接使用方法的名字而是使用类的名字
     5     def __init__(self, name='无名氏', age=20):# 属性
     6         # 给对象绑定属性
     7         self._name = name
     8         self._age = age
     9 
    10     # 我们定义一个方法就代表对象可以接收这个消息
    11     # 对象的方法的第一个参数都是统一写成self
    12     # 它代表了接收消息的对象 - 对象.消息(参数)
    13     def study(self, course):# 方法,学生的行为
    14         print('%s正在学习%s' % (self._name, course))

    2.属性,函数和方法

    实际上,方法和函数的区别表现在参数self上。方法(更准确地说是关联的方法)将其第一个参数关联到它所属的实例,因此无需提供这个参数。无疑可以将属性关联到一个普通函数,但这样就没有特殊的self参数了。

    3.隐藏

    默认情况下,可以从外部访问对象的属性。

    有些程序员认为这没问题,但有些程序员认为这违反了封装原则。他们认为应该对外部完全隐藏对象的状态(即不能从外部访问它们)。为何他们的立场如此极端?由每个对象管理自己的属性还不够吗?为何要向外部隐藏属性?毕竟,如果能直接访问CloseObject(对象c所属的类)的属性name,就不需要创建方法setName了。

    关键是其他程序员可能不知道(也不应知道)对象内部发生的情况。为避免这类问题,可以将属性定义为私有。私有属性不能从对象外部访问,而只能通过存取器方法(如set_name和get_name)来访问。

    Python没有为私有属性提供直接的支持,而是要求程序员知道在什么情况下从外部修改属性是安全的。毕竟,你必须在知道如何使用对象之后才能使用它。然而,通过玩点小花招,可以获得类似于私有属性的效果。

    要让方法或属性成为私有的(不能从外部访问),只需要让其名称以两个下划线打头即可。

    虽然以两个下划线打头有点怪异,但这样的方法类似于其他语言中的标准私有方法,然而,幕后的处理手法并不标准:在类定义中,对所有以两个下划线打头的名称都进行转换,即在开头加上一个下划线和类名。

    只要知道这种幕后处理手法,就能从类外访问私有方法,然而不应这样做。

    总之,你无法禁止别人访问对象的私有方法和属性,但这种名称修改方式发出了强烈的信号,让他们不要这样做。

    如果你不希望名称被修改,有想发出不要从外部修改属性或方法的新号,可用一个下划线打头。这虽然只是一种约定,但也有些作用。例如,from module import *不会导入以一个下划线打头的名称。

    4.类的命名空间

    下面的两条语句大致等价:

     def foo(x): return x * x

     foo = lambda x: x * x 

    它们都创建一个返回参数平方的函数,并将这个函数关联到变量foo。可以在全局(模块)作用域内定义名称foo,也可以在函数或方法内定义。定义类时情况亦如此:在class语句中定义的代码都是在一个特殊的命名空间(类的命名空间)内执行的,而类的所有成员都可访问这个命名空间。类定义其实就是要执行的代码段,并非所有的Python程序员都知道这一点,但知道这一点很有帮助。例如,在类定义中,并非只能包含def语句。

    5.指定超类

    子类扩展了超类的定义。要指定超类,可在class语句中的类名后加上超类名,并将其用圆括号括起。

     1 class Filter(object):
     2     
     3     def __init__(self):
     4         self.blocked = []
     5     
     6     def filter(self,sequence):
     7         return [x for x in sequence if x not in self.blocked]
     8     
     9 
    10 class SPAMFilter(Filter): # SPAMFilter是Fliter的子类
    11     
    12     def __init__(self): # 重写超类Filter的方法init
    13         self.blocked = ['SPAM']

    Filter是一个过滤序列的通用类。实际上,它不会过滤掉任何东西。

    Filter类的用途在于可用作其他类(如将'SPAM'从序列中过滤掉的SPAMFilter类)的基类(超类)。

    请注意SPAMFilter类的定义中有两个要点。

      ·以提供新定义的方式重写了Fliter类中方法init的定义。

      ·直接从Filter类继承了方法filter的定义,因此无需重新编写其定义。

    第二点说明了继承很有用的原因:可以创建打量不同的过滤器类,它们都从Filter类派生而来,并且都使用已编写好的方法filter。这就是懒惰的好处。

    6.继承

    要确定一个类是否是另一个类的子类,可使用内置方法issubclass。

    如果有一个类,并想知道它的基类,可访问其特殊属性__bases__。

    同样,要确定对象是否是特定类的实例,可使用isinstance。

    7.接口和内省

    一般而言,你无需过于深入地研究对象,而只依赖于多态来调用所需的方法。然而,如果要确定对象包含哪些方法或属性,有一些函数可供你来完成这种工作。

    8.抽象基类

    使用模块 abc可创建抽象基类。抽象基类用于指定子类必须提供哪些功能,却不实现这些功能。

    关于面向对象设计的一些思考

    将相关的东西放在一起。如果一个函数操作一个全局变量,最好将它们作为一个类的属性和方法。

    不要让对象之间过于亲密。方法应只关系其所属实例的属性,对于其他实力的状态,让它们自己去管理就好了。

    慎用继承,尤其是多重继承。继承有时很有用,但在有些情况下可能带来不必要的复杂性。要正确地使用多重继承很难,要排除其中的bug更难。

    保持简单。让方法短小紧凑。一般而言,应确保大多数方法都能在30秒内读完并理解。对于其余的方法,尽可能将其篇幅控制在一页或一屏内。

  • 相关阅读:
    剑指Offer-11.二进制中1的个数(C++/Java)
    剑指Offer-10.矩形覆盖(C++/Java)
    剑指Offer-9.变态跳台阶(C++/Java)
    UVA 1608 Non-boring sequence 不无聊的序列(分治,中途相遇)
    UVA1607 Gates 与非门电路 (二分)
    UVA 1451 Average平均值 (数形结合,斜率优化)
    UVA 1471 Defense Lines 防线 (LIS变形)
    UVA 1606 Amphiphilic Carbon Molecules 两亲性分子 (极角排序或叉积,扫描法)
    UVA 11134 FabledRooks 传说中的车 (问题分解)
    UVA 1152 4 Values Whose Sum is Zero 和为0的4个值 (中途相遇)
  • 原文地址:https://www.cnblogs.com/kurusuyuu/p/8588638.html
Copyright © 2011-2022 走看看