zoukankan      html  css  js  c++  java
  • Python 面向对象进阶

    Python面向对象进阶

    类的特数成员方法

    1.__doc__:类的描述信息:

    class Foo:
        """描述类信息,滴答滴答滴啊..."""
        pass
    
    f = Foo()
    print("这是类的描述信息:", f.__doc__)     # 这是类的描述信息: 描述类信息,滴答滴答滴啊..
    print("这是类的描述信息:", Foo.__doc__)     # 这是类的描述信息: 描述类信息,滴答滴答滴啊...
    
    

    2.__module__:当前操作对象所在模块:

    class Foo:
        """描述类信息,滴答滴答滴啊..."""
        pass
    
    print("当前对象的模块:", Foo.__module__)       # 当前对象的模块: __main__
    
    f = Foo()
    print("当前对象的模块:", f.__module__)       # 当前对象的模块: __main__
    

    3.__class__:当前操作对象的类:

    class Foo:
        """描述类信息,滴答滴答滴啊..."""
        pass
    
    
    print("当前对象的类:", Foo.__class__)     # 当前对象的类: <class 'type'>
    
    f = Foo()
    print("当前对象的类:", f.__class__)     # 当前对象的类: <class '__main__.Foo'>
    

    4.__init__:构造方法,通过类创建实例对象时,自动触发执行;
    5.__del__:析构方法,当对象在内存中被释放时,自动触发执行;

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

    class Foo:
        def __del__(self):
            print("执行删除!")
    
    
    f = Foo()
    print("*"*10)       # **********
    del f       # 执行删除!
    print("#"*10)       # ##########
    

    6.__call__:对象后面加括号(),触发执行;

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

    class Foo:
        def __init__(self):
            pass
    
        def __call__(self, *args, **kwargs):
            print("() is execute %s ." % "__call__")
    
    
    obj = Foo()     # 执行__init__
    obj()       # 执行__call__:() is execute __call__ .
    

    7.__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__)    # {'__module__': '__main__', 'country': 'China', '__init__': <function Province.__init__ at 0x000001B4CACC9950>, 'func': <function Province.func at 0x000001B4CACC99D8>, '__dict__': <attribute '__dict__' of 'Province' objects>, '__weakref__': <attribute '__weakref__' of 'Province' objects>, '__doc__': None}
    
    shanghai = Province("Shang Hai", 12000000)  
    # 获取对象shanghai的成员
    print(shanghai.__dict__)        # {'name': 'Shang Hai', 'count': 12000000}
    
    hongkong = Province("Hong Kong", 240000000)
    # 获取对象hongkong的成员
    print(hongkong.__dict__)        # {'name': 'Hong Kong', 'count': 240000000}
    

    8.__str__、__repr__和__format__:__str__、__repr__,改变对象的字符串显示;__format__,自定义格式化字符串;

    • __str__、__repr__
    >>> class Student:
    ...     def __init__(self, name):
    ...             self.name = name
    ... 
    >>> print(Student("Jack Ma"))
    <__main__.Student object at 0x7fedeb012e80>
    

    打印出<__main__.Student object at 0x7fedeb012e80>,不明觉厉。那么怎么才能打印出我们好读的内容呢?秩序定义好__str__()方法,返回我们想要的内容即可:

    >>> class Student:
    ...     def __init__(self, name):
    ...             self.name = name
    ...     def __str__(self):
    ...             return "Student object name: %s" % self.name
    ... 
    >>> print(Student("Jack"))
    Student object name: Jack
    

    这样打印出来的实例,不但好看,而且容易看出实例内部的重要数据。但是,细心的同学会发现直接敲变量不用print()函数,打印出来的实例还是不好看

    >>> s = Student("Pony")
    >>> s
    <__main__.Student object at 0x7fedeb0197f0>
    

    这是因为直接显示变量调用的不是__str__(),而是__repr__(),两者的区别是__str__()返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串,也就是说,repr()是为调试服务的
    解决办法是再定义一个__repr__()。但是通常__str__()和__repr__()代码都是一样的,所以,有个偷懒的写法:

    >>> class Student:
    ...     def __init__(self, name):
    ...             self.name = name
    ...     def __str__(self):
    ...             return "Student object name: %s" % self.name
    ...     __repr__ = __str__
    ... 
    >>> s = Student("LiYanhong")
    >>> s
    Student object name: LiYanhong
    
    • __format__:自定义格式化字符串:
    date_dict = {
        "ymd": "{0.year}:{0.month}:{0.day}",
        "dmy": "{0.day}/{0.month}/{0.year}",
        "mdy": "{0.month}-{0.day}-{0.year}",
    }
    
    
    class Date:
        def __init__(self, year, month, day):
            self.year = year
            self.month = month
            self.day = day
    
        def __format__(self, format_spec):
            if not format_spec or format_spec not in date_dict:
                format_spec = "ymd"
            fmt = date_dict[format_spec]
            return fmt.format(self)
    
    
    d = Date(2018, 5, 14)
    print(format(d))                # 2018:5:14
    print("{:mdy}".format(d))       # 5-14-2018
    

    9.__getitem__、__setitem__和__delitem__:用于类似字典的操作,分别表示获取、设置和删除数据:

    class Foo:
        def __getitem__(self, item):
            print("__getitem__", item)
    
        def __setitem__(self, key, value):
            print("__setitem__", key, value)
    
        def __delitem__(self, key):
            print("__delitem__", key)
    
    
    f = Foo()
    
    res = f["Jack"]             # 自动触发执行__getitem__:__getitem__ Jack
    f["Rose"] = "Marry"         # 自动触发执行__setitem__:__setitem__ Rose Marry
    del f["Jack"]               # 自动触发执行__delitem__:__delitem__ Jack
    

    10.__getattr__、__setattr__和__delattr__

    • __getattr__
      拦截点号运算,当对未定义的属性名和实例进行点号运算时,就会用属性名作为字符串调用这个方法,如果继承树可以找到该属性,则不调用此方法。

    • __setattr__
      拦截所有属性赋值语句,如果定义了这个方法,self.attr = value就会变成self.setattr("attr", value)。这个需要特别注意,当在__setattr__方法内对属性进行赋值时,不可使用self.attr=value,因为它会再次调用self.setattr("attr", value),则会形成无限递归循环,最后导致堆栈溢出异常。应该通过对属性字典做索引运算来赋值任何实例属性,也就是使用self.dict["attr"]=value。

    • __delattr__
      删除属性时触发执行;

    class Foo:
        static_var = 10
    
        def __init__(self, var):
            self.var = var
    
        def __getattr__(self, item):
            print("from __getattr__:你找的属性不存在!")
    
        def __setattr__(self, key, value):
            print("from __setattr__")
            # self.key = value  # 这就无限递归了
            # self.__dict__[key] = value    # 应该使用它
    
        def __delattr__(self, item):
            print("from __delattr__")
            # del self.item     # 无限递归了
            self.__dict__.pop(item)
    
    
    # __setattr__:添加/修改属性都会触发它的执行
    f = Foo(20)
    print(f.__dict__)   # 因为这里重写了__setattr__,凡是赋值操作都会触发它的执行,这里什么都没写,也就没有赋值,除非直接操作字典,否则永远无法赋值
    f.temp = 30
    print(f.__dict__)
    
    f.__dict__["x"] = 40    # 我们可以直接修改属性字典,来完成添加/修改属性的操作
    print(f.__dict__)
    
    # __delattr__:删除属性的时候触发执行
    del f.x
    print(f.__dict__)
    
    
    # __getattr__:只有在使用点调用属性且不存在时才会触发
    f.variable
    

    运行输出结果为:

    from __setattr__
    {}
    from __setattr__
    {}
    {'x': 40}
    from __delattr__
    {}
    from __getattr__:你找的属性不存在!
    

    isinstance和issubclass

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

    >>> class Foo:
    ...     pass
    ... 
    >>> f = Foo()
    >>> isinstance(f, Foo)
    True
    

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

    >>> class Foo:
    ...     pass
    ... 
    >>> 
    >>> class Bar(Foo):
    ...     pass
    ... 
    >>> issubclass(Bar, Foo)
    True
    

    反射

    1.反射的概念
    反射主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。
    2.反射的实现
    Python面向对象中的反射:通过字符串的形式操作对象相关的属性,Python中的一切事物都是对象,都可以使用反射。
    Python中4个可以实现自省的函数,下列方法适用于类和对象(一切皆对象,类本身也是一个对象)。

    • hasattr(object, name)
      判断一个对象里面是否有name属性或name方法,返回bool值,有name属性或方法返回true,否则返回false。需要注意的是name要用括号()括起来:
    >>> class Test:
    ...     name = "USA"
    ...     def run(self):
    ...             return "Hello World!"
    ...
    >>> t = Test()
    >>> hasattr(t, "name")
    True
    >>> hasattr(t, "run")
    True
    >>> hasattr(t, "everything")
    False
    
    • getattr(object, name, default=None)
      获取对象object的属性或方法,如果存在打印出来,如果不存在打印出默认值,默认值可选。需要注意的是,如果获取的对象方法,返回的是方法的内存地址,如果需要运行这个方法,可以在后面添加括号()执行:
    >>> class Test:
    ...     name = "Jack"
    ...     def run(self):
    ...             return "Hello World!"
    ...
    >>> t = Test()
    >>> getattr(t, "name")      # 获取name属性,存在就打印出来
    'Jack'
    >>> getattr(t, "run")       # 获取run方法,存在就打印出方法的内存地址
    <bound method Test.run of <__main__.Test object at 0x7f0d66cbee80>>
    >>> getattr(t, "run")()     # 获取run方法,后面加括号()可以执行该方法
    'Hello World!'
    >>> getattr(t, "age")       # 获取一个不存在的属性
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'Test' object has no attribute 'age'
    >>> getattr(t, "age", "18")     # 若属性不存在,返回默认值
    '18'
    
    • setattr(object, name, values)
      给对象的属性赋值,若属性不存在,先创建再赋值:
    >>> class Test:
    ...     name = "Jack"
    ...     def run(self):
    ...             return "Hello World!"
    ...
    >>> t = Test()
    >>> hasattr(t, "age")
    False
    >>> setattr(t, "age", "22")
    >>> hasattr(t, "age")
    True
    >>> getattr(t, "age")
    '22'
    
    • delattr(object, name)
      删除object对象名为name的属性,如下综合例子:
    class Intermediary:
        feature = "white"
    
        def __init__(self, name, addr):
            self.name = name
            self.addr = addr
    
        def sell_house(self):
            print("%s 卖房子" % self.name)
    
        def rent_house(self):
            print("%s 租房子" % self.name)
    
    
    intermediary = Intermediary("Wanke", "高新区")
    
    # 检测是否含有某属性
    print(hasattr(intermediary, "name"))
    print(hasattr(intermediary, "sell_house"))
    
    # 获取属性
    name = getattr(intermediary, "name")
    func = getattr(intermediary, "rent_house")
    
    # 获取不存在的属性,设置默认值
    print(getattr(intermediary, "something", "不存在的属性!"))
    
    # 设置属性
    setattr(intermediary, "flag", True)
    setattr(intermediary, "show_name", lambda self: self.name + " suffix")
    print(intermediary.__dict__)
    print(intermediary.show_name(intermediary))
    
    # 删除属性
    delattr(intermediary, "addr")
    delattr(intermediary, "show_name")
    # delattr(intermediary, "gender")     # 删除不存在的属性,报错
    
    print(intermediary.__dict__)
    

    运行输出结果为:

    True
    True
    不存在的属性!
    {'name': 'Wanke', 'addr': '高新区', 'flag': True, 'show_name': <function <lambda> at 0x000002326DA11EA0>}
    Wanke suffix
    {'name': 'Wanke', 'flag': True}
    

    万物皆对象,类也是对象

    class Foo:
        static_field = "static"
    
        def __init__(self, name):
            self.name = name
    
        def func(self):
            return "func"
    
        @staticmethod
        def bar():
            return "bar"
    
    
    print(getattr(Foo, "static_field"))     # static
    print(getattr(Foo, "func"))     # <function Foo.func at 0x000001BB676D99D8>
    print(getattr(Foo, "bar"))      # <function Foo.bar at 0x000001BB676D9A60>
    

    3.反射的好处

    • 实现可拔插机制
      可以预先定义好接口,接口只有在被调用时才会真正执行,这实现了即插即用,这其实是一种"后期绑定",即可以先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能:
    # 程序员A未实现功能
    class FtpClient:
        """ftp客户端,但是还未实现具体功能"""
        def __init__(self, addr):
            print("正在连接服务器[%s]" % addr)
            self.addr = addr
    
    但不影响程序员B继续实现其他逻辑,利用反射事先做判断
    # from module import FtpClient
    f = FtpClient("192.168.1.100")
    if hasattr(f, "get"):       # 判断方法是否实现
        func_get = getattr(f, "get")
        func_get()
    else:
        print("不存在此方法!")
        print("处理其他的逻辑!")
    
    • 动态导入模块
      动态导入模块方法:1.import;2.import importlib

    说明:
    1.函数功能用于动态的导入模块,主要用于反射或者延迟加载模块;
    2.import(module)相当于import module

    方法1实例:首先,创建目录language;然后在该目录下创建模块:lang.py,其代码为:

    class Foo:
        def __str__(self):
            return "Python language"
    

    在language目录的平级目录创建测试模块:test.py,使用__import__动态以字符串形式导入language下的lang模块:

    l = __import__("language.lang")      # 相当于import language
    python = l.lang.Foo()
    print(python)
    

    方法2实例:使用importlib进行动态导入(此方法好理解,也是官方推荐使用)

    import importlib
    la = importlib.import_module("language.lang")
    res = la.Foo()
    print(res)
    

    实例如下截图:

    元类

    1.元类的定义
    元类是用来控制如何创建类的,正如类时创建对象的模板一样,而元类的主要目的是为了控制类的创建行为。元类的实例化结果为我们用class定义的类,正如类的实例为对象(f对象是Foo类的一个实例,Foo类是type类的一个实例)。type是Python的一个内建元类,用来直接控制类生成,Python中任何class定义的类其实都是type类实例化的对象。

    2.创建类的方式

    • 使用class关键字
    class Chinese:
        country = "China"
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def talk(self):
            print("%s is talking..." % self.name)
    
    • 手动模拟class创建类的过程:将创建类的步骤拆开,手动创建
      创建类主要分为三部分:
      1.类名;2.类的父类;3.类体;
    # 类名
    class_name = "Chinese"
    # 类的父类
    class_bases = (object,)
    # 类体
    class_body = """
    country = "China"
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def talk(self):
        print("%s is talking..." % self.name)
    """
    

    步骤1(先处理类体->名称空间):类体定义的名字都会存放于类的名称空间中(一个局部的命名空间),我们可以预先定义一个空字典,然后用exec去执行类体的代码(exec产生名称空间的过程与真正的class过程类似,只是后者会将__开头的属性变形),生成类的局部名称空间,即填充字典

    class_dict = {}
    exec(class_body, globals(), class_dict)
    
    print(class_dict)
    # {'country': 'China', '__init__': <function __init__ at 0x000001EDE6851EA0>, 'talk': <function talk at 0x000001EDE8529950>}
    

    步骤2:调用元类type(也可以自定义)来产生Chinese

    Foo = type(class_name, class_bases, class_dict)     # 实例化type得到对象Foo,即我们用class定义的Foo
    
    print(Foo)                      # <class '__main__.Chinese'>
    print(type(Foo))                # <class 'type'>
    print(isinstance(Foo, type))    # True
    

    我们看到,type接受三个参数:

    • 1.第1个参数是字符串"Foo",表示类名;
    • 2.第2个参数是元组(object),表示所有的父类;
    • 3.第3个参数是字典,这里是一个空字典,表示没有定义属性和方法;

    补充:若Foo类有继承,即class Foo(Bar):...,则等同于type("Foo", (Bar,), {});一个类没有声明自己的元类,默认它的元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类;

    自定义元类精简版

    class MyType(type):
    
        def __init__(self, what, bases=None, dict=None):
            print(what, bases, dict)
    
        def __call__(self, *args, **kwargs):
            print("*"*12)
            obj = object.__new__(self)
            self.__init__(obj, *args, **kwargs)
            return obj
    
    
    class Room(metaclass=MyType):
        def __init__(self, name):
            self.name = name
    
    
    room = Room("Province")
    print(room.__dict__)
    

    运行输出结果为:

    Room () {'__module__': '__main__', '__qualname__': 'Room', '__init__': <function Room.__init__ at 0x000002007B099A60>}
    ************
    {'name': 'Province'}
    
  • 相关阅读:
    FPGA时序约束的几种方法
    使用NiosII代替SignalTap来监测FPGA内部数据
    Modelsim的使用
    Modelsim+Debussy
    ChipScope用法总结
    QuartusII增量编译的个人学习
    quartus II .qsf文件(zz)
    RAM与Nand/Nor flash之间的区别 (转)
    黑金资料AX301_A的Quartus工程建立、编译及引脚分配、程序下载
    关于sg90舵机的,要知道!要注意!
  • 原文地址:https://www.cnblogs.com/love9527/p/9024955.html
Copyright © 2011-2022 走看看