第八章:Python基础の面向对象(二)

本課主題

  • 面向对象的多态
  • 面向对象的成员
  • 成员修饰符
  • 特殊成员
  • 面向对象其他应用
  • 异常处理
  • 设计模式与单例模式 

 

面向对象的多态

  • 指定参数类型只是多态的一种表现
  • 另外一种是允许自己类型自己的子类型(典型)

以下是一个多态的例子,创建一个 func 函数,这个函数接受一个参数,我在分别传入了 字符串、列表和数字类型,因为 Python 在定义参数的时候不需要提先定义参数类型,所以它允许传入任意类型的参数,然后把它打印出来。

>>> def func(x):
...     print(x)
... 

>>> func(1)
1
>>> func([11,22,33])
[11, 22, 33]
>>> func("janice")
janice
Python多态例子

对于其他语言,比如说 Scala,这门语言必须在传入一个参数的同时,先定义它的类型,在这里我定义了参数x是必须是数字类型 (Int),以下是Scala版本的例子

scala> def func(x:Int){
     |        print(x)
     |    }
func: (x: Int)Unit
scala> func(1)


#把一组数组传入 func( ) 便会报错
scala> val array = Array(1,2,3,4)
array: Array[Int] = Array(1, 2, 3, 4)

scala> func(array)
<console>:14: error: type mismatch;
 found   : Array[Int]
 required: Int
       func(array)
Scala多态例子

传入的参数类型也可以是 Int 的派生类:意思是它们之间可以存在继承关系,且允许传入指定参数类型本身和它的子类型假设一个是父类,C是子类

scala> class A {
     |     
     | }
defined class A

scala> class B extends A{
     |     
     | }
defined class B

scala> class C extends B{
     |     
     | }
defined class C

scala> def func(c: C) {
     |     
     | }
func: (c: C)Unit

scala> val a = new A()
a: A = A@a37aefe

scala> func(a)
<console>:17: error: type mismatch;
 found   : A
 required: C
       func(a)
继承关系中的报错例子(接受C类,传入A类)
scala> class A {
     | }
defined class A

scala> class B extends A{
     | }
defined class B

scala> class C extends B{
     | }
defined class C

scala> def func(c: A) {
     | }
func: (c: A)Unit

scala> val a = new A()
a: A = A@7c8c9a05
scala> func(a)

scala> val b = new B()
b: B = B@388526fb
scala> func(b)

scala> val c = new C()
c: C = C@e70f13a
scala> func(c)
继承关系中的正确例子(接受A类,传入A|B|C类)

 

面向对象的成员

Python 字段有以下两种

  • 普通字段
  • 静态字段

首先来看看下面的代码,这个类包含了两个方法,一个字段。name 是字段 (instance variable),它是保存在对象里面。

class Foo:

    def __init__(self):
        self.name = 'janice' #这是字段(普通字段)

    def show(self):
        print(self.name)
普通字段例子

普通字段(instance variable),也会有静态字段(statics variable),静态字段是保存在類里面。

class Foo:

    CC = 123 #这是字段(静态字段)

    def __init__(self):
        self.name = 'janice' #这是字段(普通字段)
 
    def show(self):
        print(self.name)
静态字段例子

每个类里都有一个对象指针,指向这个类,比如创建了两个对象,每个对象都有 name 字段,然后他们指向同一个类,类里都有 __init__( )方法、show( )方法和一个CC静态字段,多个对象共用了一个静态字段。

 

普通字段和静态字段的分別: 

对于静态字段来说,只要一加载,就会在内存中创建,静态方法是由你来调用的,对象是用来封装数据的,如果要用来封装数据,就得创建对象。如果用不着对象就不用创建对象了,因为静态方法的调用不依赖于任何对象,这样就可以节省内存空间。创建静态方法其实也可以相当于创建普通Python函数一样。

再次說明:

  1. 普通字段是保存在对象里;静态字段是保存在類里
  2. 我们去访问成员的时候,都必需根据通用的语言规则,普通字段是用对象去访问,静态字段是用类去访问
  3. 静态字段是在代码加载的时候已经在内存中创建,但普通字段是在创建对象时才会在内存中创建。

Python方法有以下三种:

  • 普通方法,是由对象来调用的,所以至少有一個 self,因为 self 是指对象本身
    class Foo:
        CC = 123  # 这是字段(静态字段)
    
        def __init__(self):
            self.name = 'janice'  # 这是字段(普通字段)
    
        def show(self):
            print(self.name)
    
    obj = Foo()
    obj.show()
    
    """
    janice
    """
    普通方法例子
  • 静态方法,是由来调用,允许传入任意参数
    class Foo:
        CC = 123  # 这是字段(静态字段)
    
        def __init__(self):
            self.name = 'janice'  # 这是字段(普通字段)
    
        def show(self):
            print(self.name)
    
        @staticmethod
        def f1(a1,a2):
            # 静态方法
            print(a1,a2)
    
    Foo.f1(111,2222) #静态方法
    静态方法
  • 类方法,是由来调用的,至少有一個 cls
    class Foo:
        CC = 123  # 这是字段(静态字段)
    
        def __init__(self):
            self.name = 'janice'  # 这是字段(普通字段)
    
        def show(self):
            print(self.name)
    
        @classmethod
        def f2(cls):
            print("This is classmethod..")
    
    Foo.f2() #类方法
    
    """
    This is class method..
    """
    类方法例子

静态方法和类方法的区别在于你在调用类方法时不需要传入参数,它会自动把类传进去,好像普通方法的 self 一样

class Foo:
    CC = 123  # 这是字段(静态字段)

    def __init__(self):
        self.name = 'janice'  # 这是字段(普通字段)

    def show(self):
        print(self.name)

    @staticmethod
    def f1(cla, a1,a2):
        print("This is staticmethod..")
        print(cla, a1,a2)


    @classmethod
    def f2(cls):
        print("This is classmethod..")
        print(cls)


Foo.f1("abc",111,2222) #静态方法
Foo.f2() #类方法

"""
This is staticmethod..
abc 111 2222
This is classmethod..
<class '__main__.Foo'>
"""
静态方法和类方法的区别

补充说明:如果你想封装数据的话,就可以用普通方法,如果你的方法用不到对象,你就不需要创建对象了,还有一点就是静态方法也相当于 Python 的函数,这个函数的调用不依赖于任何一个对象,但也写在类里的意思表明这个方法跟这个类有关系

再次說明:所有的方法都是属于类的,而不是属于对象本身。

面向对象的属性

Python 中属性有三大功能:

  1. getter 功能 - @property
    getter 意思是通过方法来获取成员中的值。加了 @property 便把这个函数变成了一个 getter 方法,你可以不用在方法后面加上括号来执行它,此时,Python 會允許方法伪造成字段的访问形式來获取成员中的值. e.g. p.all_pager,下面是普通函数和加了@property 之后来执行方法的区别。
    class Pager:
    
    
        def __init__(self,all_count):
            self.all_count = all_count
    
        def all_pager(self):
            a1, a2 = divmod(self.all_count, 10)
    
            if a2 == 0:
                return a1
            else:
                return a1 + 1
    
    p = Pager(101)
    ret = p.all_pager()
    print(ret)
    
    #11
    普通执行函数
    class Pager:
    
    
        def __init__(self,all_count):
            self.all_count = all_count
    
        @property
        def all_pager(self):
            a1, a2 = divmod(self.all_count, 10)
    
            if a2 == 0:
                return a1
            else:
                return a1 + 1
    
    
    p = Pager(101)
    ret = p.all_pager
    print(ret)
    
    #11
    加了@property屬性

    什么时候会使用属性而不是使用普通函数呢,下面是一个例子,这里创建了一个Person类,类里面有2个字段:公开的name和私有的magic_number,因为magic_number是私有并不对外公开,​​所以一般对象根本没有任何方法能够获取这个私有字段的值,此时,开发程序的作者就可以使用Python的属性解放字段的获取功能,也不会担心字段会有被修改的机会。

    class Person():
        def __init__(self, name):
            self.name = name
            self.__magic_number = 123
    
        @property
        def ask_for_magic_number(self):
            return self.__magic_number
    
    
    peter = Person("Peter")
    
    print(peter.name)  # Peter
    # print(peter.__magic_number) #代码会报错,因为这个字段不对外公开
    print(peter.ask_for_magic_number)
    
    # Peter
    # 123
    getter的使用埸景

    如果没有加上 @property 装饰器的话,它返回的是一个对象指针

    class Pager:
    
    
        def __init__(self,all_count):
            self.all_count = all_count
    
    
        def all_pager(self):
            a1, a2 = divmod(self.all_count, 10)
    
            if a2 == 0:
                return a1
            else:
                return a1 + 1
    
    
    p = Pager(101)
    ret = p.all_pager
    print(ret)
    
    """
    <bound method Pager.all_pager of <__main__.Pager object at 0x102378940>>
    """
    沒有加@property屬性
  2. setter 功能 - @method.setter
    如果想调用 setter 方法,只需要加上一个 @method.setter 的装饰器,Python 就會允許方法伪造成字段的賦值形式 e.g. p.all_pager = 222
    class Pager:
    
    
        def __init__(self,all_count):
            self.all_count = all_count
    
        @property
        def all_pager(self):
            a1, a2 = divmod(self.all_count, 10)
    
            if a2 == 0:
                return a1
            else:
                return a1 + 1
    
    
        @all_pager.setter
        def all_pager(self, value):
            print(value)
    
    
    
    p = Pager(101)
    ret = p.all_pager
    print(ret)
    
    p.all_pager = 222
    
    #11
    #222
    @method.setter属性方法
  3. delete 功能 - @method.deleter
    最后,它也可以伪造成字段的删除形式,你只需要加上一个 @method.deleter 的装饰器,e.g. del p.all_pager

    class Pager:
    
    
        def __init__(self,all_count):
            self.all_count = all_count
    
        @property
        def all_pager(self):
            a1, a2 = divmod(self.all_count, 10)
    
            if a2 == 0:
                return a1
            else:
                return a1 + 1
    
    
        @all_pager.setter
        def all_pager(self, value):
            print("Setting the value: {}".format(value))
    
    
        @all_pager.deleter
        def all_pager(self):
            print("Deleting all_pager")
    
    
    
    p = Pager(101)
    ret = p.all_pager
    print(ret)
    
    p.all_pager = 222
    
    del p.all_pager
    @method.deleter属性方法

除了用装饰器来创建属性以外,还有一种表达属性的代码,也就是调用 property( )方法

obj = property(fget=f1, fset=f2, fdel=f3)
class Pager:

    def __init__(self,all_count):
        self.all_count = all_count

    def f1(self):
        return 123

    def f2(self,value):
        pass

    def f3(self):
        pass

    foo = property(fget=f1, fset=f2, fdel=f3)

p = Pager(101)

ret = p.foo # getter方法,然后执行 f1方法
print(ret)

p.foo = 111  # setter方法,然后执行 f2方法

del p.foo  # deleter方法,然后执行 f3方法
property( )例子一
>>> class Rectangle(object):
...     def __init__(self):
...         self.width = 0
...         self.length = 0
...     
...     def setSize(self,size):
...         self.width, self.length = size
...     
...     def getSize(self):
...        return self.width, self.length
...     
...     size = property(getSize, setSize)
... 
>>> r = Rectangle()
>>> r.width = 3
>>> r.length = 4
>>> print(r.size)
(3, 4)
>>> r.size = 30, 40
>>> print(r.width)
30
>>> print(r.length)
40
property( )例子二

这是Django的的源码例子,也有用到了property( )方法

总结,属性的诞生就是为了方便开发人员在写代码的时候,通过方法来对字段进行获取,赋值和删除的功能,而不是直接给你对这个私有字段进行修改。

 

成员修饰符

在Python 有以下两个成员修饰符

  • 公共,它是在类的外部进行调用
    class Foo:
    
        def __init__(self,name, ):
            self.name = name # 这是公共成员
    
        # 通过 f1() 来访问公共成员
        def f1(self):
            print(self.name)
    
    obj = Foo('janice')
    obj.f1() #通过调用公共方法来访问公共成员
    obj.name #直接访问公共成员
    公共修饰符例子
  • 私有,只能类自己本身成员内部访问,外部都不能访问。
    class Foo:
    
        def __init__(self,name, ):
            self.__name = name # 这个是私有成员
    
        # 通过 f1() 来访问私有成员
        def f1(self):
            print(self.__name)
    
    obj = Foo('janice') 
    obj.f1() #通过调用公有方法来访问私有成员
    #obj.name 不能直接访问私有成员,这会报错!!!
    私有修饰符例子

如果想在外部访问私有字段,这会因为访问权限不够,导致报错

class Foo:

    def __init__(self,name, ):
        self.__name = name

    # 通过 f1() 来访文字段
    def f1(self):
        print(self.__name)

obj = Foo('janice')
print(obj.name) # 会报错,因为字段变成私有字段,不对外公开访问

"""
Traceback (most recent call last):
  File "/s13/Day8/ex/s11.py", line 16, in <module>
    print(obj.name)
AttributeError: 'Foo' object has no attribute 'name'
"""
不正确运用成员修饰符(报错例子)

在父子继承关系中也不能访问私有字段 

class Foo:

    def __init__(self,name, ):
        self.__name = name

    # 通过 f1() 来访文字段
    def f1(self):
        print(self.__name)

class Bar(Foo):

    def f2(self):
        print(self.__name)


obj = Bar('janice')
obj.f2()

"""
Traceback (most recent call last):
  File "/s13/Day8/ex/s11.py", line 17, in <module>
    obj.f2()
  File "/s13/Day8/ex/s11.py", line 13, in f2
    print(self.__name)
AttributeError: 'Bar' object has no attribute '_Bar__name'
"""
继承关系中來访问私有字段(报错例子)

 ***但是在 Python里有一招绝密的招式,可以让你能够从外部访问私有字段/方法,这是一招绝密的召换术! ! !必须要在万不得而的时候才允許用,因为它并不符合典型的编程规则。

class Foo:

    def __init__(self,name, ):
        self.__name = name

obj = Foo('janice')
print(obj._Foo__name) # 访问召换术來了

#janice
绝杀秘技 - 私有字段访问召唤术

 

特殊成员

这叫 operator overloading,当有特定函数被调用的时候,Python 会自动重载函数,比如说当两个对象相加时,会重载 __add__ 方法。

  1. __init__: 在类的对象创建好之后进行变量的初始化。Python 解析为 Foo( ).__init__(obj),__init__( )方法是实例方法。
    class Foo:
    
        def __init__(self):
            print("在对象被创建的时候执行 __init__ 方法")
    
    obj = Foo()
    
    """
    在对象被创建的时候执行 __init__ 方法
    """
    __init__方法
  2. __new__: 从意义上这个才是 Python 的构造方法。它一般需要返回类的对象,当返回类的对象会自动调用 __init__方法进行初始化。如果没有返回类的对象,则__init__( ) 方法不会被调用。__new__( )方法是一个静态方法。

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    # Author: Janice Cheng
    
    class Shape(object):
    
        def __init__(object):
            print("Shape __init__: I am a Shape")
    
        def draw(self):
            pass
    
    
    class Triangle(Shape):
        def __init__(self):
            print("Triangle __init__: I am a triangle", self)
    
        def draw(self):
            print("I am drawing triangle")
    
    
    class Rectangle(Shape):
        def __init__(self):
            print("Rectangle __init__: I am a rectangle", self)
    
        def draw(self):
            print("I am drawing rectangle")
    
    
    class Trapezoid(Shape):
        def __init__(self):
            print("Trapezoid __init__: I am a trapezoid", self)
    
        def draw(self):
            print("I am drawing trapezoid")
    
    
    class Diamond(Shape):
        def __init__(self):
            print("Diamond __init__: I am a diamond", self)
    
        def draw(self):
            print("I am drawing diamond")
    class Shape(object)
    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    # Author: Janice Cheng
    
    
    from others.design_pattern import Shape
    
    class ShapeFactory(object):
    
        shapes = {
            'triangle':Shape.Triangle,
            'rectangle':Shape.Rectangle,
            'trapezoid':Shape.Trapezoid,
            'diamond':Shape.Diamond
        }
    
        def __new__(cls, name):
            if name in ShapeFactory.shapes.keys():
                print("ShapeFactory __new__: creating a new shape {} {}".format(name, cls))
                return ShapeFactory.shapes[name]()
            else:
                print("creating a new shape {}".format(name))
                return Shape.Shape()
    
    
    ShapeFactory('triangle').draw()
    ShapeFactory('rectangle').draw()
    ShapeFactory('trapezoid').draw()
    ShapeFactory('diamond').draw()
    ShapeFactory('circle').draw()
    
    """
    ShapeFactory __new__: creating a new shape triangle <class '__main__.ShapeFactory'>
    Triangle __init__: I am a triangle <others.design_pattern.Shape.Triangle object at 0x1058dd4e0>
    I am drawing triangle
    
    ShapeFactory __new__: creating a new shape rectangle <class '__main__.ShapeFactory'>
    Rectangle __init__: I am a rectangle <others.design_pattern.Shape.Rectangle object at 0x1058dd4e0>
    I am drawing rectangle
    
    ShapeFactory __new__: creating a new shape trapezoid <class '__main__.ShapeFactory'>
    Trapezoid __init__: I am a trapezoid <others.design_pattern.Shape.Trapezoid object at 0x1058dd4e0>
    I am drawing trapezoid
    
    ShapeFactory __new__: creating a new shape diamond <class '__main__.ShapeFactory'>
    Diamond __init__: I am a diamond <others.design_pattern.Shape.Diamond object at 0x1058dd4e0>
    I am drawing diamond
    
    creating a new shape circle
    Shape __init__: I am a Shape
    """
    class ShapeFactory(object)
  3. __del__: 在垃圾回收机制之前执行 __del__ 方法,也就是說在程序運行结束前的一個方法。
    class Foo:
    
        def __init__(self):
            print("在对象被创建的时候执行 __init__ 方法")
    
        def __del__(self):
            print("在垃圾回收机制之前执行 __del__ 方法")
    
    obj = Foo()
    
    """
    在对象被创建的时候执行 __init__ 方法
    在垃圾回收机制之前执行 __del__ 方法
    """"
    __del__方法
  4. __str__: 它是面向用户的,其目的是增加可读性,返回形式为用户友好性和可读性较强的字特串类型,在打印对象 print(a) 就会执行 __str__ 方法和执行str( ) 也就会执行 __str__ 方法。
    class Foo:
    
        def __str__(self):
            return "在打印对象就会执行 __str__ 方法"
    
    obj = Foo()
    print(obj)
    
    """
    在打印对象就会执行 __str__ 方法
    """
    __str__方法
  5. __repr__: 它是面向解析器的,在解析器中输入对象 >>> a 就会执行 __repr__ 方法
     class Decimal(object):
        """Floating point class for decimal arithmetic."""
        def __repr__(self):
            """Represents the number as an instance of Decimal."""
            # Invariant:  eval(repr(d)) == d
            return "Decimal('%s')" % str(self)
    
        def __str__(self, eng=False, context=None):
            """Return string representation of the number in scientific notation.
    
            Captures all of the information in the underlying representation.
            """
    
            sign = ['', '-'][self._sign]
            if self._is_special:
                if self._exp == 'F':
                    return sign + 'Infinity'
                elif self._exp == 'n':
                    return sign + 'NaN' + self._int
                else: # self._exp == 'N'
                    return sign + 'sNaN' + self._int
    
            # number of digits of self._int to left of decimal point
            leftdigits = self._exp + len(self._int)
    
            # dotplace is number of digits of self._int to the left of the
            # decimal point in the mantissa of the output string (that is,
            # after adjusting the exponent)
            if self._exp <= 0 and leftdigits > -6:
                # no exponent required
                dotplace = leftdigits
            elif not eng:
                # usual scientific notation: 1 digit on left of the point
                dotplace = 1
            elif self._int == '0':
                # engineering notation, zero
                dotplace = (leftdigits + 1) % 3 - 1
            else:
                # engineering notation, nonzero
                dotplace = (leftdigits - 1) % 3 + 1
    
            if dotplace <= 0:
                intpart = '0'
                fracpart = '.' + '0'*(-dotplace) + self._int
            elif dotplace >= len(self._int):
                intpart = self._int+'0'*(dotplace-len(self._int))
                fracpart = ''
            else:
                intpart = self._int[:dotplace]
                fracpart = '.' + self._int[dotplace:]
            if leftdigits == dotplace:
                exp = ''
            else:
                if context is None:
                    context = getcontext()
                exp = ['e', 'E'][context.capitals] + "%+d" % (leftdigits-dotplace)
    
            return sign + intpart + fracpart + exp
    class Decimal(object) 一部份源码
    >>> class Foo:
    ...     def __str__(self):
    ...         return "在打印对象就会执行 __str__ 方法"
    ...     def __repr__(self):
    ...         return "在调用对象就会执行 __repr__ 方法"
    ... 
    >>> obj = Foo()
    >>> print(obj)
    在打印对象就会执行 __str__ 方法
    >>> obj
    在调用对象就会执行 __repr__ 方法
    
    
    >>> from decimal import Decimal
    >>> a = Decimal(1.3)
    >>> a
    Decimal('1.3000000000000000444089209850062616169452667236328125')
    >>> print(a)
    1.3000000000000000444089209850062616169452667236328125
    __repr__ 方法
  6. __dict__: 获取对象中封装的所有数据或者是类中的所有方法
    class Foo:
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def show(self, name):
            print("Hello my name is ", self.name)
    
    
    obj = Foo('Janice',22)
    print(obj.__dict__)
    print(Foo.__dict__)
    
    """
    {'age': 22, 'name': 'Janice'}
    {'__doc__': None, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, 'show': <function Foo.show at 0x10f90b400>, '__init__': <function Foo.__init__ at 0x10f90b378>, '__module__': '__main__'}
    """
    __dict__方法
  7. __add__只要执行 + 就会执行 __add___方法
    class Foo:
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def __add__(self, other):
            print("只要执行 + 就会执行 __add___方法")
            res = self.name + " - " + str(other.age)
            return res
    
    
    obj1 = Foo('Janice',22)
    obj2 = Foo('Beatrice',7)
    
    ret = obj1 + obj2
    print(ret)
    
    """
    只要执行 + 就会执行 __add___方法
    Janice - 7
    """
    __add__方法
  8. __call__: 如果執行 obj( ),它就会自动去执行__call___方法
    class Foo:
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def __call__(self, *args, **kwargs):
            return "在对象后面加上括号执行 __call__ 方法"
    
    obj = Foo('Janice',22)
    ret = obj()
    print(ret)
    
    """
    在对象后面加上括号执行 __call__ 方法
    """
    __call__方法
    class Foo:
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
    obj = Foo('Janice',22)
    ret = obj()
    print(ret)
    
    """
    Traceback (most recent call last):
      File "s13/Day8/ex/test.py", line 8, in <module>
        ret = obj()
    TypeError: 'Foo' object is not callable
    """
    没有__call__方法(报错例子)
  9. __getitem__: 如果執行 obj['a'] 或者是 obj[1:5],它就會自動去執行 __getitem__方法,去创建了一个字典对象,根据索引去取值和根据切片去取值,它们返回的类型是不一样的,通过obj['a']返回的是字符串,而通过 obj[1:3] 返回的是 slice 类型。
    class Foo:
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def __getitem__(self, item):
            print(type(item))
            print("在对象后面加上中括号, obj['abc'] 执行 __getitem__ 方法!", item)
    
    
    obj = Foo('Janice',22)
    obj['abc']
    
    """
    <class 'str'>
    在对象后面加上中括号, obj['abc'] 执行 __getitem__ 方法! abc
    """
    __getitem__(返回字符串类型)
    class Foo:
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def __getitem__(self, item):
            print(type(item))
            print(item.start)
            print(item.stop)
            print(item.step)
    
            print("在对象后面加上中括号, obj['abc'] 执行 __getitem__ 方法!", item)
    
    
    obj = Foo('Janice',22)
    obj[1:10:2]
    
    """
    <class 'slice'>
    1
    10
    2
    在对象后面加上中括号, obj['abc'] 执行 __getitem__ 方法! slice(1, 10, 2)
    """
    __getitem__(返回slice类型)
  10. __setitem__: 如果執行 obj['a']=111,它就會自動去執行 __setitem__方法,进行赋值的功能。
    class Foo:
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def __setitem__(self, key, value):
            print("在对象后面加上中括号然后赋值, obj['abc'] = 111 执行 __setitem__ 方法", key,':',value)
    
    
    obj = Foo('Janice',22)
    obj['abc'] = 111
    
    """
    在对象后面加上中括号然后赋值, obj['abc'] = 111 执行 __setitem__ 方法 abc : 111
    """
    __setitem__方法
  11. __delitem__: 如果執行 del obj['a'],它就會自動去執行 __delitem__方法,
    class Foo:
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def __delitem__(self, key):
            print("在对象前面加上 del, del obj['k1'] 执行 __delitem__ 方法", key)
    
    obj = Foo('Janice',22)
    del obj['abc']
    
    """
    在对象前面加上 del, del obj['k1'] 执行 __delitem__ 方法 abc
    """
    __delitem__方法
  12. __iter__: 当你想进行的循环的时候,它是接受一个可以被迭代的东西(迭代器),才会在里面取值。
    class Foo:
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
    obj = Foo('Janice',22)
    for i in obj:
        print(i)
    
    """
    Traceback (most recent call last):
      File "s13/Day8/ex/test.py", line 10, in <module>
        for i in obj:
    TypeError: 'Foo' object is not utterable
    """
    __iter__方法(报错例子)
    class Foo:
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def __iter__(self):
            print("当你迭代一个对象的话,就会执行__iter__方法")
            # __iter__返回的是一个迭代器,所以要先执行iter( )方法
            return iter([11,22,33,44])
    
    obj = Foo('Janice',22)
    for i in obj:
        print(i)
    
    """
    当你迭代一个对象的话,就会执行__iter__方法
    11
    22
    33
    44
    """
    __iter__方法(正确例子)

    yield 是生成器,执行之后返回的是一个迭代器,就可以在里面取值了

    class Foo:
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def __iter__(self):
            yield 1
            yield 2
    
    obj = Foo('Janice',22)
    for i in obj:
        print(i)
    
    #1
    #2
    __iter__方法(结合 yield)
  13. __enter__: 进入运行时的上下文,这是上下文管理,创建对象时调用构造方法之后便调用 __enter__ 方法,返回 self 对象本身
  14. __exit__: 退出运行时的上下文,定义在块执行(或者是终止)之后上下文管理器应该做什么。创建对象时调用构造方法之后最后调用 __exit__ 方法
    class Log:
    
        def __init__(self,filename):
            self.filename=filename
            self.f=None
    
        def logging(self,text):
            self.f.write(text+'\n')
    
        def __enter__(self):
            print("__enter__")
            self.f=open(self.filename,"a+")
            return self
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            print("__exit__")
            self.f.close()
    
    # 创建Log对象的时候调用 __init__ 方法's1.txt'赋值给filename变量
    # 在这个过程会打开一个文件
    
    with Log('s1.txt') as logfile:
        print("Main") # 打印 "Main"
        logfile.logging("Test1") # 把 "Test1" 写入到在__enter__打开的文件中
        logfile.logging("Test2") # 把 "Test2" 再次写入到在__enter__打开的文件中
    
    # 完成后调用 __exit__ 方法把文件 fp.close()
    __enter__和__exit__方法
  15. __slots__: 能够限制属性的定义,它主要的目的是用来优化内存。
  16. __len__: 对象可以调用 len(obj) 方法来计算内容的长度
    class Foo:
    
        def __init__(self, name):
            self.name = name
    
        def __len__(self): # 加了这个方法,对象才可以调用len(f1)方法
            return len(self.name)
    
    
    f1 = Foo('Janice')
    print(len(f1)) # 6
    __len__( )方法
  17. __metaclass__:
class Foo:
    """
    __doc__ 就会显示这里的注
    """

    # 构造方法
    def __init__(self):
        """
        在对象创建的时候就会执行这个函数
        """
        print("在对象被创建的时候执行 __init__ 方法")

    # 析构方法
    def __del__(self):
        """
        在垃圾回收机制之前会执行这个函数
        """
        print("在垃圾回收机制之前执行 __del__ 方法")

    def __call__(self, *args, **kwargs):
        """
        让对象后面加上括号,然后就可以执行个函数
        :param args:
        :param kwargs:
        :return:
        """
        print("在对象后面加上括号执行 __call__ 方法")

    def __str__(self):
        """
        1) 在打印对象就会执行 __str__ 方法
        2) 执行str( ) 也就会执行 __str__ 方法

        没有这个方法就会打印该对象的内存地址
        :return:
        """
        return "在打印对象就会执行 __str__ 方法"

    def __add__(self, other):
        return "只要执行 + 就会执行 __add___方法"

    def __getitem__(self, item):
        print("在对象后面加上中括号, obj['abc'] 执行 __getitem__ 方法!", item)

    def __setitem__(self, key, value):
        print("在对象后面加上中括号然后赋值, obj['k1'] = 111 执行 __setitem__ 方法", key,':',value)

    def __delitem__(self, key):
        print("在对象前面加上 del, del obj['k1'] 执行 __delitem__ 方法", key)

    def __iter__(self):
        print("当你迭代一个对象的话,就会执行__iter__方法")
        return iter([11, 22, 33, 44])


# print(p.__class__) #<class '__main__.Foo'>

obj1 = Foo()
obj2 = Foo()

obj1()
print(obj1)

ret = obj1 + obj2
print(ret)

obj1['k1'] = 111

del obj1['k1']

for i in obj1:
    print(i)


"""
在对象被创建的时候执行 __init__ 方法
在对象被创建的时候执行 __init__ 方法
在对象后面加上括号执行 __call__ 方法
在打印对象就会执行 __str__ 方法
只要执行 + 就会执行 __add___方法
在对象后面加上中括号然后赋值, obj['k1'] = 111 执行 __setitem__ 方法 k1 : 111
在对象前面加上 del, del obj['k1'] 执行 __delitem__ 方法 k1
当你迭代一个对象的话,就会执行__iter__方法
11
22
33
44
在垃圾回收机制之前执行 __del__ 方法
在垃圾回收机制之前执行 __del__ 方法
"""
常用特殊成员代码例子

 

面向对象其他应用

  1. isinstance: 查看第一个参数是不是由第二个参数(类)创建的
    class Foo:
        pass
    
    class Bar(Foo):
        pass
    
    
    obj = Foo()
    ret = isinstance(obj,Bar) 
    print(ret)
    
    """
    False
    """
    isinstance( )函数例子
  2. issubclass: 查看第一个参数是不是第二个参数的子类
    class Bar:
        pass
    
    class Foo(Bar):
        pass
    
    
    obj = Foo()
    
    ret1 = issubclass(Foo,Bar) # 查看第一个参数是不是第二个参数的子类
    print(ret1)
    
    ret2 = issubclass(Bar,Foo) # 查看第一个参数是不是第二个参数的子类
    print(ret2)
    issubclass( )函数例子
  3. super( ): 主动执行父类的方法
    >>> class C1:
    ...     def f1(self):
    ...         print('c1.f1')
    ... 
    >>> class C2(C1):
    ...      def f1(self):
    ...         super(C2,self).f1()
    ...         print('c2.f1')
    ... 
    >>> obj=C2()
    >>> obj.f1()
    c1.f1
    c2.f1
    super( )函数例子
    class Person:
        def __init__(self):
            self.height = 160
    
        def about(self, name):
            print('{} is about {}'.format(name,self.height))
    
    
    class Girls(Person):
        def __init__(self):
            super(Girls,self).__init__()
            self.hobbies = 'Yoga'
    
        def about(self, name):
            print('{} is a hot girl, she is about {}, and her hobbies is {}'
                .format(name,self.height,self.hobbies))
            super(Girls,self).about(name)
    
    if __name__ == '__main__':
        jc = Girls()
        jc.about('Janice')
    
    """
    Janice is a hot girl, she is about 160, and her hobbies is Yoga
    Janice is about 160
    """
    super( )函数例子二 

源码扩展

现在回顾一下怎么导入模块然后执行函数,这里有一个index.py和setting.py的程序,然後 index.py 中執行一個 execute( )函数,是打印setting中的字符串。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: Janice Cheng

ClassName = 'Foo'
setting.py(初始版)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: Janice Cheng

from setting import ClassName

def execute():
    print(ClassName) #这是一个字符串,等于'Foo'

if __name__ == '__main__':
    execute()

"""
Foo
"""
index.py(初始版)

现在假设想调用一个Web框架里Backend的一个方法,它是在一个Foo类里,叫 f1( ),此时可以调用的Python的反射功能,这样一执行就可以在不修改别人框架的情况下把类中的方法导入然后执行

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: Janice Cheng


class Foo:

    def f1(self):
        print('Foo.f1')
commons.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: Janice Cheng

from setting import ClassName
from backend import commons

def execute():
    print(ClassName) #这是一个字符串,等于'Foo'
    cls = getattr(commons,ClassName)
    obj = cls()
    obj.f1()

if __name__ == '__main__':
    execute()

"""
Foo
Foo.f1
"""
index.py进阶版

如何在不需要修改别人源码的基础上在,在方法执行前打印一下 "Before",然后在方法执行完毕之后打印一下 "After",此时,可以运用类的继承概念来完成这个需求。

  • 首先优化一下之前的程序,运用 __import__(Path, fromlist=True) 把模块以字符串参数的形式导入。
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # Author: Janice Cheng
    
    from setting import ClassName
    from setting import Path
    
    def execute():
    
        model = __import__(Path, fromlist=True)
    
        print(ClassName) #这是一个字符串,等于'Foo'
        cls = getattr(model,ClassName)
        obj = cls()
        obj.f1()
    
    if __name__ == '__main__':
        execute()
    index.py
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # Author: Janice Cheng
    
    
    class Foo:
    
        def f1(self):
            print('Foo.f1')
    common.py
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # Author: Janice Cheng
    
    Path = 'backend.commons'
    ClassName = 'Foo'
    setting.py(重点)
  • 基于以上例子,我們有了一些启发。在lib.py中继承一下 Foo类,然后调用 super( )函数,如果现在需要调用MyFoo类中的 f1( )方法,此时就只需要修改配置文件就可以很轻松的把方法导入然后执行了。
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # Author: Janice Cheng
    
    from setting import ClassName
    from setting import Path
    
    def execute():
    
        model = __import__(Path, fromlist=True)
    
        print(ClassName) #这是一个字符串,等于'Foo'
        cls = getattr(model,ClassName)
        obj = cls()
        obj.f1()
    
    if __name__ == '__main__':
        execute()
    index.py
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # Author: Janice Cheng
    
    from backend.commons import Foo
    
    class MyFoo(Foo):
    
        def f1(self):
            print("before")
            super(MyFoo,self).f1() # 主动执行父类的 f1方法
            print("after")
    lib.py
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # Author: Janice Cheng
    
    
    class Foo:
    
        def f1(self):
            print('Foo.f1')
    common.py
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # Author: Janice Cheng
    
    Path = 'lib'
    ClassName = 'MyFoo'
    setting.py(重点)

总结:这样就可以在不修改源码的基础上执行源码的功能和我们自己新增的功能,这是一种源码功能扩展的开发技巧!!!!

 

有序字典

[更新中]

 

 

 

 

异常处理

错误是来至于语法或者是逻辑上的,语法错误指示软件的结构上有错误,导致不能被解释器解释或者是无法编译,这些错误必须在程序执行前纠正。异常是因为程序出现了错误而在正常控制流以外采取的行为,只要检测到错误并且意识到异常条件,解释器会触发一个异常,此时,Python 可以通过异常处理来捕获这些异常错误并采取相应的操作。一般异常处理的操作可以是忽略错误,或者是减轻问题影响后设法继续执行程序。常见的异常错误有以下几个:

  • NameError:尝试访问一个还没申明的变量
    #NameError: attempt to access an undeclared variable
    >>> foo
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    NameError: name 'foo' is not defined
    NameError
  • ZeroDivisionError: 除数为零错误
    >>> 1 / 0
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ZeroDivisionError: division by zero
    >>> 
    ZeroDivisionError
  • SynatxError: Python 解释器语法错误
    >>> def func()
      File "<stdin>", line 1
        def func()
                 ^
    SyntaxError: invalid syntax
    SyntaxError
  • IndexError: 请求的索引起出序列范围,在你尝试使用一个超出范围的值索引序时引发
    >>> aList = [11,22,33,44,55]
    >>> aList[2]
    33
    >>> aList[5]
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    IndexError: list index out of range
    IndexError
  • KeyError: 请求一个不存在的字典键时
    >>> aDict = {'host': 'earth', 'port': 80}
    >>> print(aDict['host'])
    earth
    >>> print(aDict['server'])
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    KeyError: 'server'
    KeyError
  • IOError: 输入输出错误
  • FileNotFoundError
    >>> f = open("blah")
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    FileNotFoundError: [Errno 2] No such file or directory: 'blah'
    FileNotFoundError
  • AttributeError: 尝试访问未知的对象属性
    >>> class Foo(object):
    ...     pass
    ... 
    >>> foo = Foo()
    >>> foo.bar = 'spam'
    >>> foo.bar
    'spam'
    >>> foo.bar2
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'Foo' object has no attribute 'bar2'
    AttributeError
  • ValueError
    >>> float('foo')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ValueError: could not convert string to float: 'foo'
    ValueError
  • TypeError
    >>> float(['this is', 1, 'list'])
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: float() argument must be a string or a number, not 'list'
    TypeError
  • IndentationError
    >>> for i in range(1,5):
    ... print(i)
      File "<stdin>", line 2
        print(i)
            ^
    IndentationError: expected an indented block
    IndentationError

这是基本的异常处理语法。

try:
    pass
except ValueError as ex:
    print(ex)
except Exception as ex:
    print(ex)
else:
    pass
finally:
    pass
  1. 这是基本的异常处理语法
    while True:
        num1 = input("num1: ")
        num2 = input("num2: ")
    
        try:
            num1 = int(num1)
            num2 = int(num2)
        except Exception as e:
            print("出现异常,信息如下:", e)
    
    """
    num1: ALEX
    num2: ALEX
    出现异常,信息如下: invalid literal for int() with base 10: 'ALEX'
    """
    异常处理(基本)
  2. 错误有很多种,如果你的程式需要基于不同的错误返回不同的功能,就需要指定错误类型。
    while True:
        num1 = input("num1: ")
        num2 = input("num2: ")
    
        try:
            li = []
            li[100]
            num1 = int(num1)
            num2 = int(num2)
            result = num1 + num2
            
        except ValueError as e1:
            print("ValueError", e1)
            
        except IndexError as e2:
            print("IndexError", e2)
            
        except Exception as e:
            print("出现异常,信息如下:", e)
    异常处理(多个异常例子)
  3. 我们也可以主动报异常,就是调用 raise Exception("") 方法
    try:
        raise Exception("主动错误一下") #self.message = '主动错误一下'
        print(1111)
    except ValueError as ex:
        print(ex)
    except Exception as ex:
        print(ex) # __str__, return self.message
    else:
        pass
    finally:
        pass
    主动报错(例子)
  4. 自定义的异常例子
    class MyException(Exception):
    
        def __init__(self, msg):
            self.message = msg
    
        def __str__(self):
            return self.message
    
    try:
        raise MyException("Janice自定义的异常")
    
    except MyException as e:
        print(e)
    
    """
    Janice自定义的异常
    """
    自定义的异常例子
  5. 断言,如果条件成立就不报错,如果条件不成立就报错
    assert 1==2    #如果条件判断 1==2 是 False 就会报错
  6. class Foo():
    
        def __init__(self,status=True):
            self.status = status
    
        def start(self):
            print("starting...")
    
    
    if __name__ == '__main__':
        f = Foo() #默认是 True
        assert f.status == True
        f.start()
    
    """
    starting...
    """
    assert 断言(成立例子)
    class Foo():
    
        def __init__(self,status=True):
            self.status = status
    
        def start(self):
            print("starting...")
    
    
    if __name__ == '__main__':
        f = Foo(False)
        assert f.status == True
        f.start()
    
    """
    Traceback (most recent call last):
      File "/s13/Day8/ex/s17.py", line 12, in <module>
        assert f.status == True
    AssertionError
    """
    assert 断言(不成立例子) 

总结:注意异常处理的粒度,应避免在 try 中放入过多的代码;谨慎使用单独的 except 语句来处理所有异常,最好能定位具体的异常;注意异常的顺序,在合适的层次处理异常

 

  

设计模式与单例模式

单例模式,总共有 23 种设计模式

什么是单例模式:一个例子,一个实例,也就是说它只有一个对象,它主要是用来创建单个实例。以下是一个数据库连接池的例子

 

 

作业

模拟人生:

  1. 定义三个人物,屌丝John,美女Liz,高富帅Peter。 John和Liz大学时是恋人,毕业工作后,Liz傍上了Peter,John伤心之余决定通过自己的努力追回Liz,多年后John通过努力变身高富帅,遇到被甩的Liz,Liz向John提出复合,John说……..(自由发挥)
  2. 定义人物信息:性别、年龄、工作、人种、国籍、特长,存款。房、车等信息,
  3. 用类实现上述故事中三者的关系和人物变化"

 Day8作业

*****************The first scene******************
Welcome to the Secondlife Land. I want to show you a little story. The story has three people, they are Liz, Peter and John. 
[y=yes][s=skip] INTRODUCTION: Want to know more about them?
>> y
[b=back] Please pick one: 0.Liz; 1.John; 2.Peter; 
>> 0
My name is Liz. I am 22 years old. I am a Sales Person. I am a Chinese. I like dancing during my lesiure time. 
I am looking for my Mr.Right, someone who loves me and accept my everything.

[b=back] Please pick one: 0.Liz; 1.John; 2.Peter; 
>> 1
My name is Peter. I am 24 years old. I am a Entrepreneur. I am a Candaian Born Chinese. I like playing sports during my lesiure time. 
I am amazing and excellent. I loves technology, I want to use technology to connect people all around the world.

[b=back] Please pick one: 0.Liz; 1.John; 2.Peter; 
>> 2
My name is John. I am 25 years old. I am a Worker. I am a Chinese. I like listening to music during my lesiure time. 
I love Liz since we were at high-school

[b=back] Please pick one: 0.Liz; 1.John; 2.Peter; 
>> b
[y=yes][s=skip] INTRODUCTION: Want to know more about them?
>> s
Select your character: 0.Liz; 1.John; 2.Peter; >> 0
*****************The second scene*****************
Imagine you are Liz...[High School Period]
Select your partner: 1.John, 2.Peter
>> 1
Liz is inlove with John
[n=next stage] What do you want to do with John:
1.kissing [love index +10]
2.talking [love index +1]
3.arguing [love index -10]
4.getting marry [only can happen when love index = 100]
5.show current love index 
>>1
Liz is kissing John
[n=next stage] What do you want to do with John:
1.kissing [love index +10]
2.talking [love index +1]
3.arguing [love index -10]
4.getting marry [only can happen when love index = 100]
5.show current love index 
>>2
What do you want to say? >> I love you
Liz says: I love you
[n=next stage] What do you want to do with John:
1.kissing [love index +10]
2.talking [love index +1]
3.arguing [love index -10]
4.getting marry [only can happen when love index = 100]
5.show current love index 
>>3
Liz is arguing with John. Love Index drop to 49 
[n=next stage] What do you want to do with John:
1.kissing [love index +10]
2.talking [love index +1]
3.arguing [love index -10]
4.getting marry [only can happen when love index = 100]
5.show current love index 
>>4
John will not marry you
[n=next stage] What do you want to do with John:
1.kissing [love index +10]
2.talking [love index +1]
3.arguing [love index -10]
4.getting marry [only can happen when love index = 100]
5.show current love index 
>>5
Your love index with John is 49
[n=next stage] What do you want to do with John:
1.kissing [love index +10]
2.talking [love index +1]
3.arguing [love index -10]
4.getting marry [only can happen when love index = 100]
5.show current love index 
>>n
*****************The third scene******************
[After witj John for a few years...]
Liz is arguing with John. Love Index drop to 39 
Liz is arguing with John. Love Index drop to 29 
Liz is arguing with John. Love Index drop to 19 
Liz is arguing with John. Love Index drop to 9 
Liz is arguing with John. Love Index drop to -1 
Since you always argue with John, you have broken up with him
*****************The fourth scene*****************
[Meet Peter.....]
You are very happy staying with Peter
All Of a Sudden.......
Liz is inlove with Peter
[n=next stage] What do you want to do with Peter:
1.kissing [love index +10]
2.talking [love index +1]
3.arguing [love index -10]
4.getting marry [only can happen when love index = 100]
5.show current love index 
>>n
*****************The fifth scene******************
[John work hard and become a Millionaire.....]
*****************The sixth scene******************
[After with Peter for a few years...]
Liz is arguing with Peter. Love Index drop to 40 
Liz is arguing with Peter. Love Index drop to 30 
Liz is arguing with Peter. Love Index drop to 20 
Liz is arguing with Peter. Love Index drop to 10 
Liz is arguing with Peter. Love Index drop to 0 
Since you always argue with Peter, you have broken up with him
******************The last scene******************
Liz told John: I want to be with you again! 
John replied........
John says: I am getting married with my fiancée

Process finished with exit code 0
程序运行后的结果 

 

 

參考資料

用断言的最佳时间: When to use assert 

 

posted @ 2016-09-24 15:16  無情  阅读(547)  评论(0编辑  收藏  举报