zoukankan      html  css  js  c++  java
  • Python中级 —— 01面向对象进阶

    面向对象进阶

    总结、补充(http://blog.csdn.net/fgf00/article/details/52479307)

    面向对象高级语法部分

      静态方法、类方法、属性方法

    类的特殊方法

    反射

    动态绑定属性,限制绑定( __slots__

    异常处理

    一、 面向对象高级语法部分

    1、静态方法(@staticmethod)

    通过@staticmethod装饰器即可把其装饰的方法变为一个静态方法。
    普通的方法,可以在实例化后直接调用,并且在方法里可以通过self.调用实例变量或类变量,但静态方法是不可以访问实例变量或类变量的,
    一个不能访问实例变量和类变量的方法,其实相当于跟类本身已经没什么关系了,它与类唯一的关联就是需要通过类名来调用这个方法.
    
    class Person(object):
        def __init__(self,name,age):
            self.name = name
            self.age = age
        @staticmethod  # 把eat方法变为静态方法
        def eat(self):
            print("%s is eating" %self.name)
    
    d = Person("xiaoming", 18)
    d.eat()
    
    

    上面的调用会出以下 错误

    TypeError: eat() missing 1 required positional argument: 'self'
    
    
    解决方法(2种):
        1. 调用时主动传递实例本身给eat方法,即 `d.eat(d)`
        2. 在eat方法中去掉self参数,但这也意味着,在eat中不能通过self.调用实例中的其它变量了   
    
    作用:只是相当于一个单纯函数,要传参数,就要把实例传进去。 
    如果说和类有关系,就是必须有类名去调用。调用不了类或实例中的任何属性
    

    2、类方法(@classmethod)

    类方法通过@classmethod装饰器实现,类方法和普通方法的区别是: 
    类方法只能访问类变量,不能访问实例变量
    
    class Person(object):
        def __init__(self,name,age):
            self.name = name
            self.age = age
        @classmethod  
        def eat(self):
            print("%s is eating" %self.name)
    
    d = Person("xiaoming", 18)
    d.eat()
    
    
    执行报错如下, ` AttributeError: type object 'Dog' has no attribute 'name' ` ,Dog没有name属性,因为name是个实例变量,类方法是不能访问实例变量的   
    
    此时可以定义一个类变量,也叫name,看下执行效果:
    
    class Person(object):
        name = "xiaohong" # 类变量
        def __init__(self,name,age):
            self.name = name
            self.age = age
        @classmethod  
        def eat(self):
            print("%s is eating" %self.name)
    
    d = Person("xiaoming", 18)
    d.eat()
    
    

    3、属性方法(@property)

    属性方法的作用就是通过@property把一个方法变成一个静态属性 (函数–>变量)
    
    class Person(object):
        name = "xiaohong" # 类变量
        def __init__(self,name,age):
            self.name = name
            self.age = age
        @property  
        def eat(self):
            print("%s is eating" %self.name)
    
    d = Person("xiaoming", 18)
    d.eat()
    
    
    可能报错  ` TypeError: 'NoneType' object is not callable `     
    
        正常调用如下:
    
    d.eat
    # 输出
    xiaoming is eating
    
    

    传参:
    属性方法赋值:
    d.eat = "baozi" 多个参数时: d.eat = "baozi", "baozi2" 接收为元组形式
    删除属性方法:
    del d.eat 报错: AttributeError: can't delete attribute
    类中定义以下方法即可:

    @eat.deleter  # 删除属性
    def eat(self):
        del self.__food
        print("Delete the finished")
    

    此时代码:

    class Person(object):
        name = "xiaohong" # 类变量
        def __init__(self,name,age):
            self.name = name
            self.age = age
        @property  
        def eat(self):
            print("%s is eating" %self.name)
        @eat.setter  # 赋值调用属性,调这个方法
        def eat(self,food):
            print("set to food:",food)
            self.__food = food
        @eat.deleter  # 删除属性
        def eat(self):
            del self.__food
            print("Delete the finished")
    
    d = Person("xiaoming", 18)
    d.eat()
    d.eat = "baozi"
    d.eat  # 传完参数后调用
    del d.eat
    d.eat  # 删完后调用
    
    

    此时报错: AttributeError: 'Dog' object has no attribute '_Dog__food' ,说明已删除。

    好吧,把一个方法变成静态属性有什么卵用呢?既然想要静态变量,那直接定义成一个静态变量不就得了么?well, 以后你会需到很多场景是不能简单通过 定义 静态属性来实现的, 比如 ,你想知道一个航班当前的状态,是到达了、延迟了、取消了、还是已经飞走了, 想知道这种状态你必须经历以下几步:

    1. 连接航空公司API查询
    2. 对查询结果进行解析
    3. 返回结果给你的用户

    因此这个status属性的值是一系列动作后才得到的结果,所以你每次调用时,其实它都要经过一系列的动作才返回你结果,但这些动作过程不需要用户关心, 用户只需要调用这个属性就可以

    二、类的特殊成员方法

    1. __doc__  表示类的描述信息

    2. __module__ __class__

    __module__ 返回当前操作的对象对应的模块名 
    __class__ 表示当前操作的对象的类名
    

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

    4. __call__ 对象后面加括号 ** 执行 **,触发执行。

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

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

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

    7. __str__ 如果一个类中定义了_str_方法,那么在打印对象时,默认输出该方法的返回值。(不定义此方法,默认返回对象地址)

    8. __iter__

    如果一个类想被用于for ... in循环,类似list或tuple那样,就必须实现一个__iter__()方法,该方法返回一个迭代对象,
    然后,Python的for循环就会不断调用该迭代对象的 `__next__()` 方法拿到循环的下一个值,
    直到遇到StopIteration错误时退出循环。
    
    我们以斐波那契数列为例,写一个Fib类,可以作用于for循环:
    
    class Fib(object):
        def __init__(self):
            self.a, self.b = 0, 1 # 初始化两个计数器a,b
    
        def __iter__(self):
            return self # 实例本身就是迭代对象,故返回自己
    
        def __next__(self):
            self.a, self.b = self.b, self.a + self.b # 计算下一个值
            if self.a > 100000: # 退出循环的条件
                raise StopIteration()
            return self.a # 返回下一个值
    
    

    结果:

    >>> for n in Fib():
    ...     print(n)
    ...
    1
    1
    2
    3
    5
    ...
    46368
    75025
    

    9. __getitem____setitem____delitem__

    用于索引操作,如字典。以上分别表示获取、设置、删除数据 (可以直接用 * 对象[] * 索引)

    class Foo(object):
        def __init__(self):
            self.data = {}
        def __getitem__(self, key):
            print('__getitem__',key)
            return self.data.get(key)
        def __setitem__(self, key, value):
            print('__setitem__',key,value)
            self.data[key] = value
        def __delitem__(self, key):
            print('__delitem__',key)
    
    obj = Foo()
    
    obj['name'] = 'fgf'   # 设置,自动触发执行 __setitem__
    print(obj.data)
    print(obj['name'])     # 获取值,自动触发执行 __getitem__
    del obj['name']        # 触发__delitem__,只是调用那个方法,具体删不删看自己配置
    
    

    10. 类的起源 __new____metaclass__ ( *type 元类 * )

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

    上述代码中,可知 f 是通过 Foo类 实例化的对象,万物皆对象,其实 Foo类 也是对象
    f 通过执行 Foo类 的构造方法创建, Foo类 通过执行 ** type ** 类的构造方法创建。
    所以type又称类的类。

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

    由此,创建类的方法就有 * 两种 * :

    1. 普通方法(通过type创建的类,只是已经封装好了): class Cat(object): pass

    2. 特殊方法:

    # 定义方法
    def func(self):
        print('hello fgf')
    
    # 创建构造函数
    def __init__(self,name,age):
        self.name = name
        self.age = age
    
    
    # 创建类
    Foo = type('Foo',(object,), {'talk': func, '__init__':__init__})
    # type第一个参数:类名
    # type第二个参数:当前类的基类(父类)
    # type第三个参数:类的成员(字典形式)
    
    f = Foo("fgf",18)
    f.talk()
    
    
    类是由type实例化产生,那么type类是如何创建的?类又是如何创建对象的?
    类中属性: **  __mateclass__** ,即用于表示该类时由谁来实例化创建。
    下面可为__metaclass__设置一个type类的派生类,从而了解类创建的过程。
    
    
    class MyType(type):
        def __init__(self, what, bases=None, dict=None):
            print("--MyType init---")
            super(MyType, self).__init__(what, bases, dict)
        def __call__(self, *args, **kwargs):
            print("--MyType call---")
            obj = self.__new__(self, *args, **kwargs)
            self.__init__(obj, *args, **kwargs)
    
    class Foo(object):
        __metaclass__ = MyType
        def __init__(self, name):
            self.name = name
            print("Foo ---init__")
        def __new__(cls, *args, **kwargs):
            # __new__是用来创建实例的,定制类,先运行new里调用init,这里写,对默认的重构
            print("Foo --new--")
            # print(object.__new__(cls))
            return object.__new__(cls)  # 返回给init,cls这代表Foo,相当于对象的self
            # 调用父类的__new__方法
    
    # 第一阶段:解释器从上到下执行代码创建Foo类
    # 第二阶段:通过Foo类创建obj对象
    obj = Foo("Fgf")
    
    

    (默认之前应该还有个myType.new)先执行myType.init,再执行myType.call,再执行Foo.new,最后Foo.init

    三、 反射

    反射(实现用户输入字符串为类的方法)

    通过字符串映射或修改程序运行时的状态、属性、方法, 有以下4个方法
    attr –> attribute [əˈtrɪbjut] 属性; (人或物的) 特征

    (直接使用的函数)

    1. hasattr(obj,name_str) :判断object中有没有一个name_str对应的方法或属性,返回True或False

    2. getattr(obj,name_str):根据字符串去获取obj对应方法的内存地址

    def getattr(object, name, default=None):
        """
        getattr(object, name[, default]) -> value
        Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y.
        """
    class Foo(object):
        def __init__(self):
            self.name = 'fgf'
        def func(self):
            print(self.name,'say Hi')
            return "func"
    
    obj = Foo()
    print(getattr(obj,'func'))
    getattr(obj, 'func')()  # # same as: obj.func()
    
    

    3. setattr(obj,name_str,value)动态把一个属性或函数装到类里面

    例:对象ojb,函数 `def func1: pass`    添加: ` setattr(obj, 'func1', func1) `
    

    4. delattr(obj, name_str) (只能)删除setattr添加的属性

    5. isinstance(obj, cls):检查是否obj是否是类 cls 的对象

    6. issubclass(sub, super):检查sub类是否是 super 类的派生类

    7. 动态导入模块

    import importlib
    
    __import__('import_lib.metaclass') #这是解释器自己内部用的
    #importlib.import_module('import_lib.metaclass') #与上面这句效果一样,官方建议用这个
    
    

    四、 动态绑定类成员,限制绑定(slots

    class Student(object):
        pass
    
    s = Student()
    
    

    - 动态绑定类成员:

    • 动态添加实例的属性 s.name = 'xiaoming'
      动态添加实例的函数 from types import MethodType
    from types import MethodType
    
    class Student(object):
        pass
    
    s = Student()
    
    def set_age(self, age):
        self.age = age
    
    s.set_age = MethodType(set_age, s) # 给实例绑定一个方法
    s.set_age(25) # 调用实例方法
    s.age # 测试结果
    
    
    < ** 此绑定只适用于单个实例,对其他实例无用。**
    

    为了给所有实例都绑定方法,可以给 * class * 绑定方法:

    • 动态绑定类成员 Student.set_score = set_score
    def set_score(self, score):
        self.score = score
    
    Student.set_score = set_score
    
    s, s2 = Student()
    
    # s, s2都可调用set_score()
    s.set_score(100)
    s.score
    
    s2.set_score(99)
    s2.score
    
    
    

    - 限制类成员 slots

    class Student(object):
    slots = ('name', 'age') # 用tuple定义允许绑定的属性名称

    注意: __slots__ 定义的属性仅对当前类实例起作用,对继承的 子类 是不起作用的:
    如果在子类中也定义__slots__,这样,子类实例允许定义的属性就是 自身的__slots__加上父类的__slots__

    五、异常处理

    在编程过程中为了增加友好性,在程序出现bug时一般不会将错误信息显示给用户,而是现实一个提示的页面

    
    try:
        diction = {}
        diction[1]
        names = []
        names[2]
    except IndexError as e:             # python3.x 里不是逗号,都是as
        print(e)
    except (KeyError,IndexError) as e:  # 采用统一处理办法
        print(e)
    except Exception as e:              # 抓住所有错误
        print(e)
    else:                               # 没出错执行这个
        pass
    finally:                            # 不管有没有错,都执行
        pass
    
    try:
        status = 1
        if status != 0 :
            raise Exception("自定义异常")
    except Exception as e:
        print(e)
    
    
  • 相关阅读:
    HTML/CSS基础教程 一
    linux鼠标闪烁问题解决
    Linux运行级别(runlevel)
    linux命令——umask
    linux命令——ulimit
    算法学习(二)——二分查找
    c++(一) :从c到c++
    shell编程(二)输入,输出和算术拓展
    shell编程(一)基础
    第二次Soring冲刺计划第一天(团队)
  • 原文地址:https://www.cnblogs.com/darksouls/p/8299463.html
Copyright © 2011-2022 走看看