zoukankan      html  css  js  c++  java
  • No.010-Python-学习之路-Day7-面向对象的进阶<类中高级方法|类创建及实例的生成过程|反射>|异常处理|动态的模块导入|断言

    类中高级方法

    静态方法<装饰器@staticmethod>

    • 静态方法仅仅名义上归类管理,但实际上静态方法未利用任何的类中属性;
    class Dog(object):
        def __init__(self, name):
            self.name = name
        @staticmethod # 静态方法->实际上跟类没什么关系,仅仅是一个方法,仅仅是需要使用类名来调用
        def eat(food):
            # 因为没有传self进去,则无法使用self.name之类的内容调用
            # 目的就是不在函数内使用
            # 相当于类的工具包
            print("Unknow dog is eating %s" %food)
            
    d = Dog("XiLuo")
    d.eat("Shit")

    类方法<装饰器@classmethod>

    • 类方法仅仅可以访问类变量,无法访问实例变量;
    class Market(object):
        discount = 10.0
        goods = {"macbook": {"sn": "No.001","price": 9600,"counts": 100},
                "iphone": {"sn": "No.002","price": 5600,"counts": 50}
        }
    
        def __init__(self, name , owner, address):
            self.name = name
            self.owner = owner
            self.address = address
    
        @classmethod
        def set_discount(cls, new_discount):
            print("Discount change from %.1f to %.1f" %(cls.discount, new_discount))
            cls.discount = new_discount
    
        def get_price(self):
            for good in self.goods:
                print("商品名-{} 编号-{} 价格-{}".format(
                    good,
                    self.goods[good]['sn'],
                    self.goods[good]['price'] * self.discount
                ))
    
    
    mymarket = Market("小苹果百货店", "Amadeus Lee", "尚阳城")
    
    mymarket.get_price() 
    mymarket.set_discount(9.0) # 可以更改类变量discount的值
    mymarket.get_price() 
    Market.set_discount(8.0) # 可以更改类变量discount的值
    Market.get_price(mymarket)
    @classmethod

    属性方法<装饰器@property>

    •  @ property 是把一个方法变成一个静态属性
    # @ property 是把一个方法变成一个静态属性
    
    # 属性方法如何赋值?
    #   使用类似于width.setter的方法来来赋值
    # 属性方法如何删除?
    #   使用类似于width.deleter的方法来删除
    # 把一个方法变成静态属性的作用:
    #   1.将直接暴露的属性隐藏,只有通过一定的限定才能够赋值或者取值,而且仍然以属性的方式调用,而非函数
    #   2.有些属性是动态的,比如航班的实时状态,如果需要取得这个值,需要执行<联系API->调用函数分析->返回结果>
    #     ,这些使用属性方法直接生成,在类外看起来就是一个属性<隐藏实现细节>,而非相对复杂的函数操作;
    
    class Screen(object):
        '''
        显示屏类,定义长和宽
        '''
        def __init__(self):
            self.__width = None
            self.__height = None
    
        @property # 将width变为属性,目的为了限制width相关的信息,希望其设置的值在范围内
        def width(self):
            return self.__width
    
        @width.setter # 可以调用方法进行修改
        def width(self, width):
            if isinstance(width, int):
                if 1366 >= width >= 1024:
                    self.__width = width
                else:
                    raise ValueError("width范围为1024-1366")
            else:
                raise ValueError("width不是int类型")
    
        @property # 将height变为属性
        def height(self):
            return self.__height
    
        @height.setter # 可以调用方法进行修改
        def height(self, height):
            if isinstance(height, int):
                if 768 >= height >= 256:
                    self.__height = height
                else:
                    raise ValueError("height的范围为256-768")
            else:
                raise ValueError("height不是int类型")
    
        @height.deleter # 当使用del xxx 的时候显示的信息;
        def height(self):
            self.__height = None
            print("已删除height!!!")
    
        @property # 这个是一个只读属性,没有定义赋值的方法哦;
        def resolution(self):
            if isinstance(self.__width, int) and isinstance(self.__height, int):
                if self.__width * self.__height == 786432:
                    return "测试通过"
                else:
                    return "测试失败"
            else:
                raise ValueError("width及height必须为int类型")
    
    
    s = Screen()
    s.width = 1024
    s.height = 768
    print(s.width, s.height, s.resolution)
    @property定义的可操作属性及只读属性

    类中的特殊方法

    • __init__(self) : obj init method;
    • __call__(self, *args, **kwargs) :Called when the instance is “called” as a function; if this method is defined, x(arg1, arg2, ...) is a shorthand for x.__call__(arg1, arg2, ...).
    • __str__(self):compute the “informal” or nicely printable string representation of an object. The return value must be a string object.When a object is Called by str(object) and the built-in functions format() and print();
    • obj/class.__doc__: return the description of the class or class of the obj;
    • obj/class.__module__: the class of obj is import from which module , return the module's name;
    • obj.__class__: return the class of the obj;
    • obj/class.__dict__ : return all attribute of a class or a obj , class only include class attrubute, not include instance attribute;obj have them all;
    class Dog(object):
        '''
            这个类是描述狗这个对象的
        '''
        type = "dog"
        def __init__(self):
            self.name = "Baby"
    
        def get_name(self):
            print("come from get_name fun", self.name)
    
        
        def __call__(self, *args, **kwargs):
            self.name = args[0]
            self.type = args[1]
    
        def __str__(self):
            return "<obj>:%s" % self.name # 可以使用打印的时候看到比如下面的print(d) # 在jango中使用较多
    
    d = Dog()
    
    d("Bruce", "Mydog") # call方法的调用1 # 如果涉及类变量不会更改,只会覆盖
    print("obj.__call__", d.name, d.type)
    Dog()("Amadeus", "yourdog") #call方法的调用2 # 如果涉及实例及类变量不会更改不会覆盖
    print("class.__call__", Dog.name, Dog.type)
    
    print(d) # 显示显示信息-->调用的是__str__(self)方法
    
    print(Dog.__doc__) # 打印这个类的描述信息
    print(d.__doc__) # 打印这个类的描述信息
    
    print("__module__",Dog.__module__, d.__module__) # 表示当前操作的对象的模块,即是从哪个模块导入的
    print("__module__",d.__class__) # 表示当前的对象属于什么类
    
    
    print(Dog.__dict__) #类调用打印类里的所有属性,不包括实例属性
    print(d.__dict__) # 实例调用,打印实例中的属性
    d.age = 2
    print(d.__dict__) # 实例调用,打印实例中的属性
    类特殊方法实例、
    • __getitem__(self, key): # 在使用obj[xxx]取值时即调用这个方法
    • __setitem__(self, key, value): # 在使用obj[key] = value赋值使,使用这个方法
    • __delitem__(self, key): # 在使用del obj[key] 时调用这个函数,具体操作自定义
    class Foo(object):  # 自封装字典 # 2.7里面可以变成列表,3.0里面没有
    
        def __init__(self):
            self.dic = {}
    
        def __getitem__(self, key): # 在使用obj[xxx]取值时即调用这个方法
            print('__getitem__', key)
            return self.dic[key]
    
        def __setitem__(self, key, value): # 在使用obj[key] = value赋值使,使用这个方法
            print('__setitem__', key, value)
            self.dic[key] = value
    
        def __delitem__(self, key): # 在使用del obj[key] 时调用这个函数,具体操作自定义
            print('__delitem__', key)
            self.dic.pop(key)
    
    obj = Foo()
    obj['name'] = "Bruce"
    obj['age'] = 19
    del obj['name']
    print(obj)
    try:
        print(obj['name'])
    except KeyError as KeyNotFound:
        print("key-name已经被删除!")
    print(obj['age'])
    自封装字典类型

     类的创建及实例的生成

    class Foo(object):  # 定义一个类
        def __init__(self, name):
            self.name = name
    f = Foo("Bruce") # 实例化这个类

    以上代码中,f是对象, Foo是类;在python中一切皆对象,那么Foo这个类本身也是一个对象,是对象就有相应的类,那么它的类是什么呢?

    >>> type(f)
    <class '__main__.Foo'>  # 可以看到对象f的class是 Foo
    >>> type(Foo)    
    <class 'type'> # 可以看到对象Foo的class是type

    以上代码中,类Foo身为对象时的类是type;也就是说我们可以直接通过实例化一个type类来创建Foo,如下:

    # 定义一个单独的函数
    def func(self):
        print('hello Bruce!!')
    # 定义一个单独的函数
    def init(self, name, age):
        self.name = name
        self.age = age
    
    # 使用以上参数生成一个type类的对象Foo
    Foo = type('Fooo', (object,), {'talk':func, '__init__':init}) 
    # 可以看到如正常的Foo类一样
    print(type(Foo))
    f = Foo('Bruce', 12)
    print(f.age)

     So...从上面所有的来看,obj<f>的class是Foo类,Foo类的类是type.......type呢则是python解释器自己来创建的;

    事实上这也是我们在使用class关键字的时候Python所做的事情。Python通过使用metaclass-<这里是metaclass = type>来实现这一过程。

    一个类的创建过程详解

    __metaclass__ :

    • 源类,指定源类的参数,是用来生成类的类,比如type即是源类;
    •  影响class初始化过程、修改class的内容,返回修改过的class
    • 一般用来做一些逻辑比较复杂的操作,比如自省,修改继承,以及改变类的默认attribute比如__dict__等;

    __new__(cls, *args, **kwargs):

    • 用来创建一个类的实例的,起创建一个类实例的作用构造器(constructor);
    • new第一个参数为cls,因为new工作时self并不存在,所以要传入一个类<cls>用于创建对象<self>;

    __init__(self, *args, **kwargs):

    • 用来初始化一个实例的,起初始化一个已被创建的实例的作用, 初始化(initializer);

    class Mytype(type):
    
        def __init__(self, what, bases=None, dict=None):
            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)
         return obj
    
    class Foo(object, metaclass=Mytype):  # 源类,即类的类,可以重写, 在python3中需要写在class内部
    
        __metaclass__ = Mytype
    
        def __new__(cls, *args, **kwargs):
            print("Foo __new__")
            self = object.__new__(cls)  # 建立对象,返回对象的内存地址# 继承父类的__new__()方法,不继承无法实例化
            print(self)
            return self
    
        def __init__(self):
            self.name = "Bruce"
    
    
    f = Foo() 
    # Foo()中的“()”是Foo作为实例call其类<源类>的__call_方法,即本质是呼叫源类 Mytype中的__call__方法;
    # 而在__call__方法中,传入了Mytype的对象即<self = Foo>,并以此调用Foo.__new__及Foo.__init__方法来创建Foo类的对象;
    # __new__方法通过使用父类中的__new__方法成功建立了一个对象<开辟了内存空间>并将其返回, 则 obj = __new__.return即新生成的对象<空内存区域>;
    # 随后调用__init__方法,传入obj<__new__生成的对象的指针>及相关变量值,完成类实例的初始化,并将obj返回,则f = obj,完成整个Foo的实例的创建;
    # 详解在这里面
    '''https://stackoverflow.com/questions/100003/what-are-metaclasses-in-python'''

     再具体点如下:

    class MyType(type):
        def __init__(self,*args,**kwargs):
    
            print("Mytype __init__",*args,**kwargs)
            super(MyType, self).__init__(*args, **kwargs)
    
        def __call__(self, *args, **kwargs):
            print("Mytype __call__", *args, **kwargs)
            obj = self.__new__(self)
            print("obj ",obj,*args, **kwargs)
            print(self)
            self.__init__(obj,*args, **kwargs)
            return obj
    
        def __new__(cls, *args, **kwargs):
            print("Mytype __new__",*args,**kwargs)
            return type.__new__(cls, *args, **kwargs)
    
    
    def __new__(cls, *args, **kwargs):
        print("Foo __new__",cls, *args, **kwargs)
        return object.__new__(cls)
    def __init__(self,name):
        self.name = name
    
    # 生成MyType的实例Foo,则会先调用MyType类的__new__,然后再调用__init__完成Foo的创建
    Foo = MyType('Foo', (object, ), {"__new__": __new__, "__init__": __init__})
    # 生成Foo的实例f,过程如上
    f = Foo("Bruce")
    print("f",f)
    print("fname",f.name)
    完整的实例创建过程

    Metaclass可以不是一个类,也可以是一个函数,如下:

    def myMeta(class_name , bases, class_attributes):
        print("class_name-{}
    bases-{}
    class_attributes-{}".format(
            class_name, bases, class_attributes
        ))
        return type(class_name, bases, class_attributes)
    # 从这里可以看出定义类的关键字class,其真实操作即:
    # Foo = metaclass(class_name="Foo", bases=(object,), class_attributes={})
    class Foo(object, metaclass=myMeta):
    
        def __init__(self, name):
            self.name = name
    
        def get_name(self):
            print(self.name)
    f = Foo("Bruce")
    f.get_name()
    一个函数作为metaclass

    metaclass的一个应用,我希望所有自定义类的属性名字都是大写,可以使用metaclass来实现:

    class MyMeta(type):
    
        def __new__(cls, class_name, base, attributes):
            upper_attributes = {}
    
            for key , var in attributes.items():
                if key.startswith("__"):
                    upper_attributes[key] = var
                else:
                    upper_attributes[key.upper()] = var
            print(upper_attributes)
            return super(MyMeta, cls).__new__(cls, class_name, base, upper_attributes)
    
    class Foo(object, metaclass=MyMeta):
        def __init__(self):
            self.name = "Bruce"
        def get_name(self):
            print(self.name)
    f = Foo()
    print(hasattr(f, "GET_NAME"))
    print(hasattr(f, "get_name"))
    metaclass使新建类属性名大写

    反射

    反射->通过字符串映射或修改程序运行时的状态、属性、方法,有以下4个方法:

    hasattr(object, name):

    • 判断object对象中是否存在name属性
    • getattr(object, name[, default]):
    • 获取object对象的属性的值,如果存在则返回属性值,如果不存在分为两种情况,一种是没有default参数时,会直接报错;
    • 给定了default参数,若对象本身没有name属性,则会返回给定的default值;

    getattr(object, name[, default])

    • 返回object的名字为str{name}的attribute方法;
    • 如果没有这个属性,返回default的值<如果设置>,否则报错;

    setattr(object, name, value)

    • 给object对象的name属性赋值value,如果对象原本存在给定的属性name,则setattr会更改属性的值为给定的value;
    • 如果对象原本不存在属性name,setattr会在对象中创建属性,并赋值为给定的value;

    delattr(object, name)

    • 用来删除指定对象的指定名称的属性,和setattr函数作用相反;

    callable(object)

    • 函数用于检查一个对象是否是可调用的;
    • 返回 True,object 仍然可能调用失败;但如果返回 False,调用对象 object 绝对不会成功;
    • 对于函数、方法、lambda 函式、 类以及实现了 __call__ 方法的类实例, 它都返回 True;
    def bulk(self):
        print("%s is yelling..." )
    class Dog(object):
        def __init__(self, name):
            self.name = name
        def eat(self):
            print("%s is eating ..." % self.name)
    d = Dog("Vick")
    choice = input(">>:").strip() # 用户通过输入调用instance的方法;
    if hasattr(d, choice): # 判断在某个instance中是否有这个字符串对应的方法 # 如果是属性呢,属性无法执行,如何而从新赋值呢?setattr
        print(getattr(d, choice)) # 映射出了eat函数对的内存对象地址
        getattr(d,choice)() # 然后执行
    else:
        setattr(d, choice, bulk) # 动态装入一个方法,也可以动态装入一个属性
        getattr(d, choice)(d) # 因为没在类中 self没有自动传入
    delattr(d, "eat") # 相当于 del d.eat 删除object中的某一属性

     异常处理

    常用的异常之类,后期补充

    • AttributeError 试图访问一个对象没有的树形,比如foo.x 但是foo没有x
    • IoError 输入/输出一场,基本上是无法打开文件
    • ImportError 无法引入模块或包;基本上是路径问题或名称错误
    • IndentationError 语法错误(的子类);代码缩进有问题
    • IndexError index超出界限
    • KeyError 试图访问字典里不存在的键
    • KeyboardInterrupt Ctrl+C 被按下
    • NameError 使用一个还未被赋予对象的变量
    • SyntaxError 代码不能编译,一般是语法错误
    • TypeError 传入对象类型与要求的不符合
    • UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于令有一个同名的全局变量,导致你以为正在访问它
    • ValueError 传入一个调用者不期望的值,即使值的类型是正确的

     异常抓取具体语法:

    try:
        #data['name']
        #name[3]
        open('test.txt')
        a = 1
        print(a)
    except (KeyError, IndexError) as ke: # 一次判断多个错误,但是不知道是哪个代码出错了,一般不这么写
        print("Index或者key不存在", ke)          # 可以用在不管出来什么错误,处理方法都是相同的
    except Exception as e:  # 可以抓住所有错误,都可以抓住 # 前面写明细,后面抓所有,防止出现前面未抓到的某些错误
                            # 注意IndentationError SyntaxError 这种是无法抓的,因为根本编译不了
        print("出错了", e)
    else: # 没有出现错误的时候执行这个
        print("一切正常")
    finally: # 不管有没有错误都会执行
        print("不管有没有错误,都执行")

    自定义异常并触发

    class BruceException(Exception):
        def __init__(self, msg):
            self.message = msg
        def __str__(self): # 父类中存在这个方法,即return self.message
             return super(BruceException, self).__str__()
    try:
        raise BruceException('数据库连不上') # 使用raise直接触发自己写的异常
    except BruceException as e:
        print(e)
        
    # 题外话#####__str__的作用,即在str(obj)的时候生效###
    fault = BruceException("My fault!!") # 实例化
    print(str(fault))  # 调用obj的__str__方法  ##,当然不止str()可以触发,只要将obj-->str即会触发;

     动态的模块导入

    正常导入操作如下:

    import lib.aa 

    如果我有以下的需求如何实现呢?

    # 已知
    modname = "aa"
    # 需要导入lib.aa,尝试:
    from lib import modname #这样是无效的 类似于from lib import "aa"

    以上需求有两种方式,第一种方式为python解释内部机制使用,如下:

    modname = "lib.aa"
    lib = __import__(modname) # 只导入lib的级别
    print(lib)
    lib.aa.myfun()

    另外一种为正常代码编写过程中使用,如下:

    import importlib
    modname = "lib.aa"
    mod = importlib.import_module(modname) # 写到哪层就是哪层
    print(mod)
    mod.myfun()

     断言

    name = "Bruce"
    assert type(name) is str # 断言正确接着执行
    print("aaa")
    assert type(name) is int # 断言错误报错
    print("bbb")
    # 断言的作用在于,之后的程序依赖于前面的断言通过,而程序相对重要,不能出错;

    end

  • 相关阅读:
    但是难道我就不能在JBuilder里面看我可爱的中文了吗?[小糊涂的灵感]
    why is j2me midp superior to wap?[小糊涂的灵感]
    NRF51822之GPIOTE介绍
    Unity脚本生命周期
    树状数组学习笔记
    模版—扩展欧几里德
    hdu4339Query
    POJ3273Monthly Expense
    多校第一场
    ZOJJ Watashi's BG3631
  • 原文地址:https://www.cnblogs.com/FcBlogPythonLinux/p/12295200.html
Copyright © 2011-2022 走看看