• 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目录 

      

  • 相关阅读:
    jQuery(2)
    jQuery(1)
    underscore.js
    面向对象复习
    1.14函数复习
    面向对象(3)继承
    10.18
    1017
    js笔记二
    js笔记一
  • 原文地址:https://www.cnblogs.com/nopnog/p/6994420.html
Copyright © 2020-2023  润新知