zoukankan      html  css  js  c++  java
  • object类

    object是一个基类,或称之为元类。在python2.x上,不继承object类的称之为经典类,继承了object类的称之为新式类。

    在python3种默认都是新式类,也即是所有的自定义类、基类都会继承object类。

    object类里面内置了许多函数,下面一一讲解:

    1. __dir__:返回一个列表,其中包含所有的属性和方法名(包含特殊方法)。函数原型如下:

    def __dir__(self, *args, **kwargs):
        """ Default dir() implementation. """
        pass

       下面举一个例子:

    class Test():
        pass
    
    print(Test().__dir__())  # 也可执行 dir(obj) 来获取所有属性和方法
    
    """
    输出如下:
    ['__module__', '__dict__', '__weakref__', '__doc__', '__repr__', 
     '__hash__', '__str__', '__getattribute__', '__setattr__', '__delattr__', 
     '__lt__', '__le__', '__eq__', '__ne__', '__gt__', 
     '__ge__', '__init__', '__new__', '__reduce_ex__', '__reduce__', 
     '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__']
    """
    

    2. __setattr__:当我们执行obj.属性名=属性值setattr(obj,属性名,属性值),即为属性赋值时被调用。

    class Foo(object):
        def __init__(self):
            self.name = 'Alex'   # 打印 call __setattr__
    
        # 重写__setattr__,为了使调用时打印输出,obj.xxx = value时调用
        def __setattr__(self, key, value):
            print('call __setattr__')
            return object.__setattr__(self, key, value)  # 调用基类的方法来赋值
    
    f = Foo()
    f.name = 'Jone'  # 打印 call __setattr__
    

    3. __getattribute__和__getattr__:使用类的对象进行obj.属性名getattr(obj,属性名)来取对象属性的值的时候被调用,不管属性是否存在,

       __getattribute__方法都会被调用。如果属性存在,则返回该属性的值,如果属性不存在,则返回None。我们在使用hasattr(obj,属性名)来判断

       某个属性是否存在时,__getattribute__方法也会被调用。

       与__getattr__的区别:

       __getattribute__方法不管属性是否存在,都会被调用。而__getattr__只在属性不存在时调用,默认会抛出:

       'AttributeError: xxx object has no attribute yyy'这样的错误,但我们可以对其进行重写,做我们需要的操作。

    class Test(object):
        def __init__(self):
            self.name = 'Alex'
    
        def __getattribute__(self, item):
            print("call __getattribute__")
            return object.__getattribute__(self, item)
    
        def __getattr__(self, item):
            print('call __getattr__')
            print("%s不存在,但我可以返回一个值" % item)
            return 54
    
    f = Test()
    print(f.name)  # name 属性存在,打印 call __getattribute__
    print(f.age)   # age 属性不存在,打印 call __getattribute__,会返回None,
                   # 然后打印 call __getattr__, 但 __getattr__ 方法返回了54(如果没有被重写,则直接报异常),所以这里打印54。
    

    4. __str__和__repr__:默认打印的是对象的内存地址等信息。

       1)只重写__str__,则只能用于定义print(obj)时打印的内容,交互式命令行输出obj还是函数地址。

    >>> class Foo(object):
    ...     def __str__(self):
    ...         return "我是Foo"
    ...
    >>> f1 = Foo()
    >>> print(f1)
    我是Foo
    >>> f1
    <__main__.Foo object at 0x0000023BF701C550>

       2)只重写__repr__,则用于同时定义python命令行输出obj的内容,以及print(obj)的打印内容。

    >>> class Foo(object):
    ...     def __repr__(self):
    ...         return "我是Foo"
    ...
    >>> f1 = Foo()
    >>> print(f1)
    我是Foo
    >>> f1
    我是Foo

       3)如果__str__和__repr__都被重写了,则__str__负责print的信息,而__repr__负责命令行直接输出obj的信息。

    >>> class Foo():
    ...     def __str__(self):
    ...         return "我是Foo---str"
    ...     def __repr__(self):
    ...         return "我是Foo---repr"
    ...
    >>> f1 = Foo()
    >>> print(f1)
    我是Foo---str
    >>> f1
    我是Foo---repr
    

      

    5. __new__:__new__方法是一个静态方法,在调用时,传入你需要实例化的类名以及初始化参数列表,创建一个cls类的对象,并返回。其函数原型如下:

    @staticmethod
    def __new__(cls, *more):
        """ Create and return a new object.  See help(type) for accurate signature. """
        pass
    

       注意以下几点:

        1)__new__在object被指定为@staticmethod,但更像是一个@classmethod,第一个参数传入类本身cls。

        2)__new__在__init__之前运行,为传入的类(Foo)生成一个实例并返回。

        3)__init__在__new__之后执行,为__new__返回的类实例进行初始化。

        4)__init__是一个实例方法,是由实例来调用的。所以要执行__init__方法,必须先要由__new__生产一个实例。这就是为什么先执行__new__方法的原因。

    class Foo(object):
        # 后于__new__方法执行,为__new__方法生成的对象进行初始化
        def __init__(self, name, age):  # __new__返回的对象作为self传入__init__
            print("执行__init__方法")
            self.name = name
            self.age = age
    
        # __new__方法先于__init__方法执行,用于生成一个指定类的对象
        def __new__(cls, *args, **kwargs):  # 接收参数cls为Foo类,然后是f1 = Foo("Alex",age=32)里的name和age
            print("执行__new__方法", args, kwargs)
            ret = object.__new__(cls)  # 调用__new__生成一个Foo对象
            print(ret)                 # 打印 <__main__.Foo object at 0x000001AD868F8668>
            return ret                 # 返回生成的 Foo 对象
    
    f1 = Foo("Alex",age=32)
    
    """
    output:
    执行__new__方法 ('Alex',) {'age': 32}
    <__main__.Foo object at 0x000001B6523A64F0>
    执行__init__方法
    """
    

    6. __gt__、__lt__、__ge__、__le__:这几个都是用于比较大小的,我们可以对其进行重写,来自定义对象如何比较大小。它们的原型如下: 

        # 判断是否大于等于 greater than or equal,在obj >= other时调用
        def __ge__(self, *args, **kwargs):
            """ Return self >= value. """
            pass
    
        # 判断是否大于 greater than,在obj > other时调用
        def __gt__(self, *args, **kwargs):
            """ Return self > value. """
            pass
    
        # 判断是否小于等于 less than or equal,在obj <= other时调用
        def __le__(self, *args, **kwargs):
            """ Return self <= value. """
            pass
    
        # 判断是否小于 less than,在obj<other时调用
        def __lt__(self, *args, **kwargs):
            """ Return self < value. """
            pass
    

       下面举一个简单的例子:

    class Car():
        def __init__(self, price):
            self.price = price
    
        def __lt__(self, other):
            print("execute __lt__")
            return self.price < other.price
    
    c1 = Car(100)
    c2 = Car(200)
    if c1 < c2:
        print("car1 is cheaper.")
    else:
        print("car2 is cheaper.")
    

      

    7. __eq__和__hash__:__hash__实际上是返回一个int值(计算类中所有成员的hash值),用来唯一标记这个对象。户自定义类中,如果你没有实

       现__eq____hash__函数,那么class会继承到默认的__eq____hash__函数。

       先来解释下什么是可hash对象:如果一个对象是可哈希的,那么在它的生存期内必须不可变(需要一个哈希函数__hash__),而且可以和其他对象

       较(需要比较方法__eq__).相同的对象一定有相同的哈希值。

       1)不可hash:list, set, dict

       2)可hash:数值,字符串,boolean

       3)自定义类对象可不可hash:当类中只含可hash的成员时,该类定义的对象是可hash的。当我们实现的类想成为不可hash的类,则可以重写__eq__方法,

          然后不重写__hash__,__hash__方法会被置None,该类的对象就不可hash了。如果不重写__eq__,则会使用基类的实现,比较对象时会比较它们的hash值。

    class Foo(object):
        def __init__(self):
            self.name = 'Alex'
    
        def __eq__(self, other):
            return self.name == other.name
    
    f1 = Foo()
    f2 = Foo()
    print(f1 == f2)  # True
    print(f1 is f2)  # False
    print(hash(f1) == hash(f2))  # 抛出异常TypeError错误,因为此时__hash__=None
    

    8. __dict__属性:在 Python 类的内部,无论是类属性还是实例属性,都是以字典的形式进行存储的,其中属性名作为键,而值作为该键对应的值。

       为了方便用户查看类中包含哪些属性,Python 类提供了 __dict__ 属性。该属性可以用类名或者类的实例对象来调用。

           1)每个类有自己的__dict__属性,就算存着继承关系,父类的__dict__ 并不会影响子类的__dict__,两者不共用。子类有自己的__dict__, 父类也有自

              己的__dict__,子类的类属性、静态方法、普通方法、内置属性等放在子类的dict中,父类的放在父类dict中。

           2)实例对象也有自己的__dict__属性, 存储 self.xxx 信息(只有属性,不包含方法),与1)不同的是父子类对象公用__dict__

           3)内置的数据类型没有__dict__属性。

       我们在查找一个类属性或者对象属性的时候,也是通过__dict__字典来查的,查找顺序如下:

           1)首先查找实例 obj.__dict__。

           2)如果1)中未找到,则去查找类 type(obj).__dict__。

           3)如果2)也未找到,则去查找父类的 __dict__,查找顺序也是先实例后类。

    class Test(object):
        x = 1   # 类属性
    
        def __init__(self, y):
            self.y = y  # 实例属性
    
        def fun(self):
            print(self.y)
    
    
    t = Test(2)
    print(t.__dict__)
    """
    通过实例调用__dict__,输出如下:
    {'y': 2}
    """
    
    print(Test.__dict__)
    """
    因为__dict__不共用,所以输出内容不会涉及到基类object
    { 
        '__module__': '__main__', 
        'x': 1, 
        '__init__': <function Test.__init__ at 0x0000021E999C83A0>, 
        'fun': <function Test.fun at 0x0000021E999C8430>, 
        '__dict__': <attribute '__dict__' of 'Test' objects>, 
        '__weakref__': <attribute '__weakref__' of 'Test' objects>, 
        '__doc__': None
    }
    
    """
    
    print(vars(t))  # __dict__ 也可以用 vars 函t数替代,功能完全相同
    
    t.fun()                       # 在 type(t).__dict__ 中寻找,找到了直接返回
    print(t.y)                    # 在 t.__dict__ 中寻找,找到了 y 直接返回
    print(t.__dict__['y'])        # 上面的调用机制实际上是这样的
    
    print(t.x)                    # 在t.__dict__中找不到x,于是到type(t).__dict__中找到了x,并返回其值
    print(type(t).__dict__['x'])  # 上面的调用机制实际上是这样的
    
  • 相关阅读:
    文件的上传
    自定义EL表达式的函数
    JSTL 自定义标签
    Java c3p0连接池之二
    Java c3p0连接池
    JSP 登录与注册的小案例
    Java jdbc 连接oracle之三(封装工具类)
    Java jdbc 连接oracle之二(使用properties文件)
    Swift中Notification.Name自定义枚举
    swift UITableViewCell 策划删除,iOS11之后 设置侧滑不到最左边
  • 原文地址:https://www.cnblogs.com/yanghh/p/13155966.html
Copyright © 2011-2022 走看看