• Python 面向对象


    本章内容     

      1、面向对象简介

      2、析构函数

      3、封装

      4、继承

      5、多态

      6、类方法

      7、特殊的成员方法 

      8、反射

      9、异常处理

      10、动态载入

    面向对象简介  

      面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。

      面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度。

          而面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。

          在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class)的概念。

     

      以Student类为例,在Python中,定义类是通过class关键字:

    class Student(object):
       n = 3 #类变量
       age = 11 #类变量
    def __init__(self, name, score,age): #构造函数,在实例化的时候做一些类的初始化工作 self.name
    = name #实例变量(静态属性),作用域就是实例的本身 self.score = score
    self.age = age def print_score(self): print(
    '%s: %s' % (self.name, self.score))

      class后面紧接着是类名,即Student,类名通常是大写开头的单词,

      紧接着是(object),表示该类是从哪个类继承下来的,通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。

      定义好了student类,就可以通过student类,创造出student的实例,

      定义好了Student类,就可以根据Student类创建出Student的实例,创建实例是通过类名+()实现的: 

    >>> bart = Student()
    >>> bart
    <__main__.Student object at 0x10a67a590>
    >>> Student
    <class '__main__.Student'>

      变量bart指向的就是一个Student的object,后面的0x10a67a590是内存地址,每个object的地址都不一样,Student本身则是一个类。

      __init__()方法是可选的,如果不提供,Python 会给出默认的__init__方法

      注意到__init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。

      有了__init__方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数,但self不需要传,Python解释器自己会把实例变量传进去

      和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别,所以,你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数

      变量的取值范围

    a1 = Student('python','99','20')
    print('age,n',a1.age,a1.n)
    
    
    #运行结果
    age,n 20 3
    
    #age取的是实例变量,n取的是类变量
    #结论,取的时候先取实例变量,如果没有的话择取类变量

      增加类变量

    ##bart.high = '1.8m'
    ##bar.high
      '1.8m'

       删除类变量

    del a1.name
    print(a1.name)
    
    #运行结果
    AttributeError: 'Student' object has no attribute 'name'

      修改类变量

    a1 = Student('python','99','20')
    a1.n = 18
    
    print('from a1 n',a1.n)
    
    a2 = Student('c++','88','10')
    print('from a2 n',a2.n)
    
    #在a1中修改了n的值,那n2种会不会有影响呢?
    #运行结果:
    from a1 n :18
    from a2 n :3
    
    #实验证明,a1,a2各自有各自的内存空间,内存空间内存放着自己的变量,a1修改的是自己内存空间内部的,所以a2,不受影响。
    #注意,这里的n如果是列表的话,a1修改,a2是能看到的,这里是有与list的类型有关

       问个问题,类变量的用途?

       当我听到这个问题的时候,有点懵逼,怎么总结呢?alex做的总结,--->大家公用的属性,定义一个就可以了,节省开销。这个场景应用与cn = 'china',如果14亿中国人都要放到构造函数中吗?当然不必要,直接一个类函数即可。

    析构函数

       在实例释放、销毁的时候自动执行的,通常用于做一些收尾的工作,如关闭一些数据库链接,或关闭打开的一些临时文件

    class Student(object):
    
        def __init__(self, name, score,age):   
            self.name = name          
            self.score = score
            self.age = age
    
        def print_score(self):
            print('%s: %s' % (self.name, self.score))
    
        def __del__(self):
            print('del -------')

    a1 = Student('python','99','22')
    del a1

    上面的“__del__”就是一个析构函数了,当使用del 删除对象时,会调用他本身的析构函数,另外当对象在某个作用域中调用完毕,在跳出其作用域的同时析构函数也会被调用一次,这样可以用来释放内存空间

      __del__()也是可选的,如果不提供,则Python 会在后台提供默认析构函数

    封装

       私有属性/方法

      私有属性参考 http://www.cnblogs.com/blackmatrix/p/5600830.html

      其实,Python并没有真正的私有化支持,但可用下划线得到伪私有。   尽量避免定义以下划线开头的变量!

       (1)_xxx      "单下划线 " 开始的成员变量叫做保护变量,意思是只有类实例和子类实例能访问到这些变量,需通过类提供的接口进行访问;不能用'from module import *'导入

            (2)__xxx    类中的私有变量/方法名 (Python的函数也是对象,所以成员方法称为成员变量也行得通。)," 双下划线 " 开始的是私有成员,意思是只有类对象自己能访问,连子类对象也不能访问到这个数据。

            (3)__xxx__ 系统定义名字,前后均有一个“双下划线” 代表python里特殊方法专用的标识,如 __init__()代表类的构造函数。  

    继承

    class People():
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
        def talk(self):
            print('%s is talking ....' %self.name)
    
        def eat(self):
            print('%s is eating ....'%self.name)
    
    
    class man(People):
        def strong(self):
            print('%s is strong'%self.name)
    
    
    m1 = man('alex','22')
    m1.strong()
    m1.talk()
    
    #运行结果
    alex is strong
    alex is talking ....

      这就实现了man对people的继承,能实现了继承,那能对父类的重构呢?

    class People():
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
        def talk(self):
            print('%s is talking ....' %self.name)
    
        def eat(self):
            print('%s is eating ....'%self.name)
    
    
    class man(People):
        def strong(self):
            print('%s is strong'%self.name)
    #  重构父类的talk方法        
        def talk(self):
            People.talk(self)
            print('man is talking...')
    
    m1 = man('alex','22')
    m1.strong()
    m1.talk()
    
    #运行结果
    alex is strong
    alex is talking ....
    man is talking...
    重构父类方法

    如果类中想增加变量,而继承的父类中个数有限,那需要怎么办?那就重构构造函数

    class People():
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
        def talk(self):
            print('%s is talking ....' %self.name)
    
        def eat(self):
            print('%s is eating ....'%self.name)
    
    class man(People):
        def __init__(self,name,age,hoby):
            #对构造函数重构的两种方法。
            #People.__init__(self,name,age)
            super(man,self).__init__(name,age)
            print('My hoby is %s'%hoby)
    
        def strong(self):
            print('%s is strong'%self.name)
    
        def talk(self):
            People.talk(self)
            print('man is talking...')
    
    m1 = man('alex','22','basketball')
    m1.strong()
    m1.talk()
    
    #运行结果
    alex is strong
    alex is talking ....
    man is talking...
    重构构造函数

      经典类&&新式类

      class People     : 经典类

      class People(object)     :新式类

          People.__init__(self,name,age)      :经典类

      super(man,self).__init__(name,age)     :新式类

        多重继承

    class People():
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
        def talk(self):
            print('%s is talking ....' %self.name)
    
        def eat(self):
            print('%s is eating ....'%self.name)
    
    class relation():
        def makefriend(self,obj):
            print('%s is make friend with %s'%(self.name,obj.name))
    
    #多重继承,继承People,与relation
    class man(People,relation):
        def __init__(self,name,age,hoby):
            super(man,self).__init__(name,age)
            print('My hoby is %s'%hoby)
    
    class woman(People):
        def born(self):
            print('%s born a body'%self.name)
    
    
    m1 = man('alex','22','basketball')
    w1 = woman('hanmeimei','21')
    
    m1.makefriend(w1)
    多重继承

        python2  经典类是按照深度优先来继承的,新式类是按照广度优先来继承的

      python3  都是通过广度优先来继承的

       测试是广度优先还是深度优先的方法:

    __author__ = "Alex Li"
    
    class A:
        def __init__(self):
            print("A")
    class B(A):
        pass
        # def __init__(self):
        #     print("B")
    class C(A):
        pass
        # def __init__(self):
        #     print("C")
    class D(B,C):
        pass
        # def __init__(self):
        #     print("D")
    
    
    obj = D()
    测试方法

    多态

      一种方法,多种实现  

    class Animal:
        def __init__(self, name):  # Constructor of the class
            self.name = name
    
        def talk(self):  # Abstract method, defined by convention only
            pass #raise NotImplementedError("Subclass must implement abstract method")
    
        @staticmethod
        def animal_talk(obj):
            obj.talk()
    
    class Cat(Animal):
        def talk(self):
            print('Meow!')
    
    
    class Dog(Animal):
        def talk(self):
            print('Woof! Woof!')
    
    
    d = Dog("陈荣华")
    #d.talk()
    
    c = Cat("徐良伟")
    #c.talk()
    #
    # def animal_talk(obj):
    #     obj.talk()
    
    Animal.animal_talk(c)
    Animal.animal_talk(d)
    多态code

    类方法

      静态方法

      通过@staticmethod装饰器即可把其装饰的方法变为一个静态方法,什么是静态方法呢?其实不难理解,普通的方法,可以在实例化后直接调用,并且在方法里可以通过self.调用实例变量或类变量,但静态方法是不可以访问实例变量或类变量的,一个不能访问实例变量和类变量的方法,其实相当于跟类本身已经没什么关系了,它与类唯一的关联就是需要通过类名来调用这个方法

    class Dog(object):
        def __init__(self,name):
            self.name = name
    
        @staticmethod      #把eat方法变为静态方法
        def eat(self):
            print("%s is eating"% self.name)
    
    
    d1 = Dog('puppy')
    d1.eat()
    
    
    #改变为静态方法后,在运行的话就会有个错误
    #TypeError: eat() missing 1 required positional argument: 'self'
    
    解决办法:
    1. 调用时主动传递实例本身给eat方法,即d.eat(d) 
    2. 在eat方法中去掉self参数,但这也意味着,在eat中不能通过self.调用实例中的其它变量了

       类方法

       类方法通过@classmethod装饰器实现,类方法和普通方法的区别是, 类方法只能访问类变量,不能访问实例变量

    class Dog(object):
    
        #name = '我是类变量'
        def __init__(self,name):
            self.name = name
    
        @classmethod     #使之成为类方法
        def eat(self):
            print("%s is eating"% self.name)
    
    
    d1 = Dog('puppy')
    d1.eat()
    
    
    #运行的时候会有个报错
    #AttributeError: type object 'Dog' has no attribute 'name'
    
    解决方法:
    如果把上面的类变量打开,那就问题了

      属性方法

      属性方法的作用就是通过@property把一个方法变成一个静态属性

    class Dog(object):
    
        def __init__(self,name):
            self.name = name
    
        @property          #使之变为属性方法
        def eat(self):
            print("%s is eating"% self.name)
    
    
    d1 = Dog('puppy')
    d1.eat()
    #d1.eat
    
    #运行结果会有报错
    #TypeError: 'NoneType' object is not callable
    
    解决办法:
    直接d1.eat   来调用

      属性方法中的增加变量,删除属性

    class Dog(object):
    
        def __init__(self,name):
            self.name = name
            self.__age = None
        @property          #使之变为属性方法
        def eat(self):
            print("%s is eating"% self.name,self.__age)
    
        @eat.setter       #属性方法中,再添加参数
        def eat(self,age):
            print("set age to " ,age)
            self.__age= age   #对值进行保存
    
        @eat.deleter     #删除属性的方法
        def eat(self):
            del d1.__age
            print("删除完毕!")
    
    d1 = Dog('puppy')
    
    d1.eat
    d1.eat = '11'      #对属性进行复制
    d1.eat
    
    del d1.eat      #如果不添加eat.deleter ,直接调用这个方法会报错的

      属性方法的应用场景

    class Flight(object):
        def __init__(self,name):
            self.flight_name = name
    
        def checking_status(self):
            print("checking flight %s status " % self.flight_name)
            return  1
    
        @property
        def flight_status(self):
            status = self.checking_status()
            if status == 0 :
                print("flight got canceled...")
            elif status == 1 :
                print("flight is arrived...")
            elif status == 2:
                print("flight has departured already...")
            else:
                print("cannot confirm the flight status...,please check later")
    
    
    f = Flight("CA980")
    f.flight_status
    
    #直接运行f.flight_status 就可以看到接口的查询结果了,相当于内部封装好,外部直接调用,和闭包很相像
    调用航空的机票查询接口

    类的特殊成员方法

      1、__doc__  表示类的描述信息

    class Foo:
        """ 描述类信息,这是用于看片的神奇 """
     
        def func(self):
            pass
     
    print Foo.__doc__
    #输出:类的描述信息,这个注释必须放到类名的下面
    View Code

      2、__module__和__class__

    #放到包下
    class C:
    
        def __init__(self):
            self.name = 'wupeiqi'
    
    
    #在包外面
    from lib.aa import C
    
    obj = C()
    print obj.__module__  # 输出 lib.aa,即:输出模块
    print obj.__class__      # 输出 lib.aa.C,即:输出类
    View Code

      3、__init__ 构造方法,通过类创建对象时,自动触发执行。

      4、__del__析构方法,当对象在内存中被释放时,自动触发执行。


    注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的

      5、__call__ 对象后面加括号,触发执行。


    注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

    class Foo:
     
        def __init__(self):
            pass
         
        def __call__(self, *args, **kwargs):
     
            print '__call__'
     
     
    obj = Foo() # 执行 __init__
    obj()       # 执行 __call__
    View Code

      6. __dict__ 查看类或对象中的所有成员

    class Province:
        country = 'China'
    
        def __init__(self, name, count):
            self.name = name
            self.count = count
    
        def func(self, *args, **kwargs):
            print
            'func'
    
    
    # 获取类的成员,即:静态字段、方法、
    print(Province.__dict__)
    # 输出:{'country': 'China', '__module__': '__main__', 'func': <function func at 0x10be30f50>, '__init__': <function __init__ at 0x10be30ed8>, '__doc__': None}
    
    obj1 = Province('HeBei', 10000)
    print(obj1.__dict__)
    # 获取 对象obj1 的成员
    # 输出:{'count': 10000, 'name': 'HeBei'}
    
    obj2 = Province('HeNan', 3888)
    print(obj2.__dict__)
    # 获取 对象obj1 的成员
    # 输出:{'count': 3888, 'name': 'HeNan'}
    View Code

      7、__str__ 如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。

    class Foo:
     
        def __str__(self):
            return 'alex li'
     
     
    obj = Foo()
    print obj
    # 输出:alex li
    View Code

      8、__getitem__、__setitem__、__delitem__

    用于索引操作,如字典。以上分别表示获取、设置、删除数据,有啥用?还不知道

    class Foo(object):
     
        def __getitem__(self, key):
            print('__getitem__',key)
     
        def __setitem__(self, key, value):
            print('__setitem__',key,value)
     
        def __delitem__(self, key):
            print('__delitem__',key)
     
     
    obj = Foo()
     
    result = obj['k1']      # 自动触发执行 __getitem__
    obj['k2'] = 'alex'   # 自动触发执行 __setitem__
    del obj['k1'] 
    View Code

      9、__new__ __metaclass__

    class Foo(object):
     
        def __init__(self,name):
            self.name = name
     
    f = Foo("alex")

    print type(f) #输出:<class '__main__.Foo'> 表示,obj 对象由Foo类创建
    print type(Foo) #输出:<type 'type'> 表示,Foo类对象由 type类创建

    上述代码中,obj 是通过 Foo 类实例化的对象,其实,不仅 obj 是一个对象,Foo类本身也是一个对象,因为在Python中一切事物都是对象。如果按照一切事物都是对象的理论:obj对象是通过执行Foo类的构造方法创建,那么Foo类对象应该也是通过执行某个类的 构造方法 创建。

      所以,f对象是Foo类的一个实例Foo类对象是 type 类的一个实例,即:Foo类对象 是通过type类的构造方法创建。那么,创建类就可以有两种方式:

      1、普通方法

    class Foo(object):
      
        def func(self):
            print 'hello alex'

      2、特殊方法

    def func(self):
        print 'hello wupeiqi'
      
    Foo = type('Foo',(object,), {'func': func})
    #type第一个参数:类名
    #type第二个参数:当前类的基类
    #type第三个参数:类的成员

      特殊方法加上构造函数

    def func(self):
        print("hello %s"%self.name)
    
    def __init__(self,name,age):
        self.name = name
        self.age = age
    Foo = type('Foo',(object,),{'func':func,'__init__':__init__})
    
    f = Foo("jack",22)
    f.func()
    特殊方法加上构造函数

    反射

      反射即想到4个内置函数分别为:getattr、hasattr、setattr、delattr  获取成员、检查成员、设置成员、删除成员下面逐一介绍先看例子:

    class Foo(object):
        def __init__(self):
            self.name = 'abc'
    
        def func(self):
            return 'ok'
    
    
    obj = Foo()
    
    #获取并执行成员
    res = getattr(obj,'func')()
    print(res)
    #运行结果:ok
    
    #检查成员
    res = hasattr(obj,'func')
    print(res)
    #运行结果,因为有func,返回True
    
    #设置成员值
    print(obj.name)
    res = setattr(obj,'name',19)
    print(obj.name)
    #运行结果:
    abc
    19
    
    #删除成员
    print(obj.name)
    delattr(obj,'name')
    print(obj.name)
    #运行结果
    #AttributeError: 'Foo' object has no attribute 'name'

      如果setattr没有这个属性,那如何添加

    class Foo(object):
        def __init__(self,name):
            self.name = name
    
        def func(self):
            return 'ok'
    
    
    def talk(self):
        print('%s is talking ...'%self.name)
    obj = Foo('Python')
    
    str = 'buck'
    res = setattr(obj,str,None)
    print(res)
    #如果第三位指定None或者其他字符,则可以直接返回
    
    setattr(obj,str,talk)
    res = getattr(obj,str)
    res(obj)
    #如果第三位指定的是个函数,那需要用上面的这个方法

    异常处理

      在编程过程中为了增加友好性,在程序出现bug时一般不会将错误信息显示给用户,而是现实一个提示的页面,通俗来说就是不让用户看见大黄页!!!

    python2
    try:
        pass
    except Exception,ex:
        pass
    
    python3
    try:
        pass
    except Exception as ex:     #万能异常,捕获所有的异常
        pass
    
    终极版
    try:
      pass
    except (aerror,berror) as e:
      pass
    except cerror as e:
      pass
    else:
      print('没有错误,则我执行,一切正常')
    finally:
      print('无论什么情况我都会执行')

      主动出发异常

    try:
        raise Exception('错误了。。。')
    except Exception,e:
        print e

      自定义异常 

    class WupeiqiException(Exception):
     
        def __init__(self, msg):
            self.message = msg
     
        def __str__(self):
            return self.message
     
    try:
        raise WupeiqiException('我的异常')
    except WupeiqiException,e:
        print e

      断言

    # assert 条件
     
    assert 1 == 1
     
    assert 1 == 2
    
    
    用断言判断前面的条件,只有断言的条件正确,才执行后面的程

       需要注意的是,有些错误是捕捉不到的,如语法错误,缩进错误,导致pythno还没解析到捕捉错误的代码处就停止了,所以就不会有后面的捕捉动作了。

     动态载入

        动态引入,已字符串的形式引入。

         

       动态导入这个模块,两种方式

       1、lib = __import__('lib.aa')

           lib.aa.C().aa       #就可以实现调用了

      2、import importlib

         lib = importlib.import_modoule('lib.aa')

               lib.C().name          #这样就可以调用了,这是建议用法,比上面多引入了一层

    返回python目录 

      

  • 相关阅读:
    POJ3159 Candies —— 差分约束 spfa
    POJ1511 Invitation Cards —— 最短路spfa
    POJ1860 Currency Exchange —— spfa求正环
    POJ3259 Wormholes —— spfa求负环
    POJ3660 Cow Contest —— Floyd 传递闭包
    POJ3268 Silver Cow Party —— 最短路
    POJ1797 Heavy Transportation —— 最短路变形
    POJ2253 Frogger —— 最短路变形
    POJ1759 Garland —— 二分
    POJ3685 Matrix —— 二分
  • 原文地址:https://www.cnblogs.com/nopnog/p/6994420.html
Copyright © 2020-2023  润新知