zoukankan      html  css  js  c++  java
  • 第039 类和对象 拾遗

    课题笔记

    组合

    所谓的组合就是把类的实例化放到一个新类里面,那就把旧类给组合进去了。

    组合一般就是把几个不是很有继承关系的、没有直线关系的,横向关系的几个类放到一起。

    class Turtle:
        def __init__(self, x):
            self.num = x
    
    class Fish:
        def __init__(self, x):
            self.num = x
    
    class Pool:
        def __init__(self, x, y):
            self.turtle = Turtle(x)
            self.fish = Fish(y)
    
        def print_num(self):
            print('水池里总共有乌龟 %d 只,小鱼 %d 条' %(self.turtle.num, self.fish.num))

    Python的特性支持很流行的编程模式,叫‘Mix-in’,‘混入’的意思。[扩展阅读] Python Mixin 编程机制(转)


     [扩展阅读] Python Mixin 编程机制(转)

    Mixin 简介

    Mixin 编程是一种开发模式,是一种将多个类中的功能单元的进行组合的利用的方式,这听起来就像是有类的继承机制就可以实现,然而这与传统的类继承有所不同。通常 Mixin 并不作为任何类的基类,不关心与什么类一起使用,而是在运行时动态的同其他零散的类一起组合使用。

    特点

    使用 Mixin 机制有如下好处:

      • 可以在不修改任何源代码的情况下,对已有类进行扩展
      • 可以保证组件的划分
      • 可以根据需要,使用已有的功能进行组合,来实现“新”类;
      • 很好的避免了类继承的局限性,因为新的业务需要可能就需要创建新的子类。

    多继承

    Python支持多继承,即一个类可以继承多个子类。可以利用该特性,可以方便的实现mixin继承。如下代码,类A,B分别表示不同的功能单元,C为A,B功能的组合,这样类C就拥有了类A, B的功能。

    class A:
        def get_a(self):
        print 'a'
    
    class B:
        def get_b(self):
        print 'b'
    
    class C(A, B): 
        pass
    
    c = C()
    c.get_a()
    c.get_b()

    __bases__

    多继承的实现就会创建新类,有时,我们在运行时,希望给类A添加类B的功能时,也可以利用python的元编程特性__bases__属性便在运行时轻松给类A添加类B的特性,如下代码:

    A.__bases__ += (B,)
    a.get_b()

    其实__bases__也是继承的机制,因为__bases__属性存储了类的基类。因此多继承的方法也可以这样实现:

    class C:
        pass
    
    C.__bases__ += (A, B, )

    插件方式

    以上两种方式,都是基于多继承和python的元编程特性,然而在业务需求变化时,就需要新的功能组合,那么就需要重新修改A的基类,这回带来同步的问题,因为我们改的是类的特性,而不是对象的。因此以上修改会对所有引用该类的模块都受到影响,这是相当危险的。通常我们希望修改对象的行为,而不是修改类的。同样的我们可以利用__dict__来扩展对象的方法

    class PlugIn(object):
        def __init__(self):
            self._exported_methods = []
            
        def plugin(self, owner):
            for f in self._exported_methods:
                owner.__dict__[f.__name__] = f
    
        def plugout(self, owner):
            for f in self._exported_methods:
                del owner.__dict__[f.__name__]
    
    class AFeature(PlugIn):
        def __init__(self):
            super(AFeature, self).__init__()
            self._exported_methods.append(self.get_a_value)
    
        def get_a_value(self):
            print 'a feature.'
    
    class BFeature(PlugIn):
        def __init__(self):
            super(BFeature, self).__init__()
            self._exported_methods.append(self.get_b_value)
    
        def get_b_value(self):
            print 'b feature.'
    
    class Combine:pass
    
    c = Combine()
    AFeature().plugin(c)
    BFeature().plugin(c)
    
    c.get_a_value()
    c.get_b_value()

    类、类对象和实例对象

    3个不同的东西。首先分析一段代码 :

    >>> class C:
        count = 0
    
        
    >>> a = C()
    >>> b = C()
    >>> c = C()
    >>> c.count += 10
    >>> c.count
    10
    >>> a.count
    0
    >>> b.count
    0
    >>> C.count
    0
    >>> C.count += 100
    >>> a.count
    100
    >>> b.count
    100
    >>> c.count
    10

     

     类定义与类对象的属性都是静态属性,对应的就是大C里面的count,类属性和类对象是相互绑定的,并不会依赖于下面小写的实例对象;所以当c.count += 10 的时候并不会影响到上面类对象的属性,只是改变了它自身,c里面多了一个示例属性,把自己所属的类的类属性给覆盖了。

    属性名如果和方法名相同,属性会覆盖方法:

    >>> class C:
        def x(self):
            print('X-man!')
    
            
    >>> c = C()
    >>> c.x()
    X-man!
    >>> c.x = 1
    >>> c.x
    1
    >>> c.x()
    Traceback (most recent call last):
      File "<pyshell#8>", line 1, in <module>
        c.x()
    TypeError: 'int' object is not callable
    >>> 

    为了避免这个错误出现,应该遵守一些约定俗成。例如:

    1、不要试图在一个类里边定义出所有能想到的特性和方法,应该利用继承机制组合机制来进行扩展;

    2、用不同的词性命名,如属性用名词,方法名用动词

    到底什么是绑定?

    Python严格要求方法需要有实例才能被调用,这种限制其实就是Python所谓的绑定的概念。

    >>> class BB:
        def printBB():
            print('no zuo no die')
    
            
    >>> BB.printBB()
    no zuo no die
    >>> bb = BB()
    >>> bb.printBB()
    Traceback (most recent call last):
      File "<pyshell#15>", line 1, in <module>
        bb.printBB()
    TypeError: printBB() takes 0 positional arguments but 1 was given
    >>> 

    所以在定义时需要一个self!!!

    __dict__ 是可以查看对象所拥有的属性,

    >>> class CC:
        def setXY(self, x, y):
            self.x = x
            self.y = y
        def printXY(self):
            print(self.x, self.y)
    
            
    >>> dd = CC()
    >>> dd.__dict__
    {}
    >>> CC.__dict__
    mappingproxy({'__module__': '__main__', 'setXY': <function CC.setXY at 0x000001F32F331040>, 'printXY': <function CC.printXY at 0x000001F32F3311F0>, '__dict__': <attribute '__dict__' of 'CC' objects>, '__weakref__': <attribute '__weakref__' of 'CC' objects>, '__doc__': None})
    >>> dd.setXY(4, 5)
    >>> dd.__dict__
    {'x': 4, 'y': 5}
    >>> CC.__dict__
    mappingproxy({'__module__': '__main__', 'setXY': <function CC.setXY at 0x000001F32F331040>, 'printXY': <function CC.printXY at 0x000001F32F3311F0>, '__dict__': <attribute '__dict__' of 'CC' objects>, '__weakref__': <attribute '__weakref__' of 'CC' objects>, '__doc__': None})
    >>> del CC
    >>> ee = CC()
    Traceback (most recent call last):
      File "<pyshell#31>", line 1, in <module>
        ee = CC()
    NameError: name 'CC' is not defined
    >>> dd.printXY()
    4 5
    >>> #房子已经造好了,把图纸删了不影响房子的使用

     

    课后测试题及答案

    测试题:

    0. 什么是组合(组成)?

    组合就是在定义一个类的时候,在里面使用其他类,把一个类的实例化放到一个新类里面。

    答:Python 继承机制很有用,但容易把代码复杂化以及依赖隐含继承。因此,经常的时候,我们可以使用组合来代替。在Python里组合其实很简单,直接在类定义中把需要的类放进去实例化就可以了。

    例子:

    // 乌龟类
    class Turtle:
        def __init__(self, x):
            self.num = x
    // 鱼类
    class Fish:
        def __init__(self, x):
            self.num = x
    // 水池类
    class Pool:
        def __init__(self, x, y):
            self.turtle = Turtle(x)        // 组合乌龟类进来
            self.fish = Fish(y)        // 组合鱼类进来
         
        def print_num(self):
            print("水池里总共有乌龟 %d 只,小鱼 %d 条!" % (self.turtle.num, self.fish.num))
    
    >>> pool = Pool(1, 10)
    >>> pool.print_num()

    1. 什么时候用组合,什么时候用继承?

    继承用在新类与基类是垂直关系,就是有许多相似的地方可以继承的;而组合主要是水平关系,本身几个类没有多少联系,但是需要用到的话就可以将其组合进来。

    答:根据实际应用场景确定。简单的说,组合用于“有一个”的场景中,继承用于“是一个”的场景中。例如,水池里有一个乌龟,天上有一个鸟,地上有一个小甲鱼,这些适合使用组合。青瓜是瓜,女人是人,鲨鱼是鱼,这些就应该使用继承啦。 A

    2. 类对象是在什么时候产生?

    在类定义完后,使用类来示例化一个实例对象的时候吧。

    答:当你这个类定义完的时候,类定义就变成类对象,可以直接通过“类名.属性”或者“类名.方法名()”引用或使用相关的属性或方法。

    3. 如果对象的属性跟方法名字相同,会怎样?

    对象的属性会覆盖掉方法,是覆盖掉这个示例对象的方法,

    答:如果对象的属性跟方法名相同,属性会覆盖方法。

    class C:
            def x(self):
                    print('Xman')
    
    >>> c = C()
    >>> c.x()
    Xman
    >>> c.x = 1
    >>> c.x
    1
    >>> c.x()
    Traceback (most recent call last):
      File "<pyshell#20>", line 1, in <module>
        c.x()
    TypeError: 'int' object is not callable

    4. 请问以下类定义中哪些是类属性,哪些是实例属性?

    class C:
            num = 0
            def __init__(self):
                    self.x = 4
                    self.y = 5
                    C.count = 6

    num是属性,C.count是类属性, x、y是示例属性。

    答:num 和 count 是类属性(静态变量),x 和 y 是实例属性。大多数情况下,你应该考虑使用实例属性,而不是类属性(类属性通常仅用来跟踪与类相关的值)。对应动动手第0题?

    5. 请问以下代码中,bb 对象为什么调用 printBB() 方法失败?

    class BB:
            def printBB():
                    print("no zuo no die")
    
    >>> bb = BB()
    >>> bb.printBB()
    Traceback (most recent call last):
      File "<pyshell#8>", line 1, in <module>
        bb.printBB()
    TypeError: printBB() takes 0 positional arguments but 1 was given

    printBB()他这里定义是不需要一个self就是一个对象的,就是bb.printBB() 相当于 bb.print(bb),它是会传入一个bb参数的,但是你定义的这个类不需要参数

    答:因为 Python 严格要求方法需要有实例才能被调用,这种限制其实就是 Python 所谓的绑定概念。所以 Python 会自动把 bb 对象作为第一个参数传入,所以才会出现 TypeError:“需要 0 个参数,但实际传入了 1 个参数“。

    正确的做法应该是:

    class BB:
            def printBB(self):
                    print("no zuo no die")
    
    >>> bb = BB()
    >>> bb.printBB()
    no zuo no die

    动动手:

    0. 思考这一讲我学习的内容,请动手在一个类中定义一个变量,用于跟踪该类有多少个实例被创建(当实例化一个对象,这个变量+1,当销毁一个对象,这个变量自动-1)。

    __dict__ 是看属性的。。。。。。应该用类的属性来跟踪与类相关的值

    python类中的self参数和cls参数

    1. self表示一个类的实例对象本身。如果用了staticmethod就无视这个self了,就将这个方法当成一个普通的函数使用了。

    2. cls表是这个类本身。

    >>> class C:
        count = 0
        def __init__(self):
            C.count += 1
        def __del__(self):
            C.count -= 1
    
            
    >>> a = C()
    >>> b = C()
    >>> c = C()
    >>> C.count
    3
    >>> del a
    >>> C.count
    2
    >>> del b, c
    >>> C.count
    0
    >>> 

    1. 定义一个栈(Stack)类,用于模拟一种具有后进先出(LIFO)特性的数据结构。至少需要有以下方法:

    方法名 含义
    isEmpty() 判断当前栈是否为空(返回 True 或 False)
    push() 往栈的顶部压入一个数据项
    pop() 从栈顶弹出一个数据项(并在栈中删除)
    top() 显示当前栈顶的一个数据项
    bottom() 显示当前栈底的一个数据项
    class Stack(object):
        def __init__(self, start = []): #这里初始化,如果一开始就有几个元素就先压入栈内
            self.stack = [] # 新建一个stack空列表
            for x in start:
                self.push(x)
    
        def isEmpty(self):
            return not self.stack
    
        def push(self, obj):
            self.stack.append(obj)
    
        def pop(self):
            if not self.stack:
                print('警告:栈为空!')
            else:
                return self.stack.pop()
    
        def top(self):
            if not self.stack:
                print('警告:栈为空!')
            else:
                return self.stack[-1]
    
        def bottom(self):
            if not self.stack:
                print('警告:栈为空!')
            else:
                return self.stack[0]

    作者:Agiroy_70

    本博客所有文章仅用于学习、研究和交流目的,欢迎非商业性质转载。

    博主的文章主要是记录一些学习笔记、作业等。文章来源也已表明,由于博主的水平不高,不足和错误之处在所难免,希望大家能够批评指出。

    博主是利用读书、参考、引用、抄袭、复制和粘贴等多种方式打造成自己的文章,请原谅博主成为一个无耻的文档搬运工!

  • 相关阅读:
    题解【DP100题1~10】
    新博客已建好!
    题解【语文1(chin1)- 理理思维】
    题解【[BJOI2012]算不出的等式】
    题解【[HAOI2006]受欢迎的牛】
    题解【[FJOI2018]所罗门王的宝藏】
    Redis常用命令
    mysql table 最新更新时间
    中国翻译史阶记
    HTTP Session原理
  • 原文地址:https://www.cnblogs.com/hale547/p/13354807.html
Copyright © 2011-2022 走看看