NMT Data Mining & Big Data

面向对象编程(OOP)之系列4

2019-06-07
NMt

issubclass()&isinstance()、type()&dir()、属性之增删改查、算数运算符重载、str()&repr()

@@@@

本文是来源于上雷俊丽老师python课的课件。

14.issubclass() & isinstance()

issubclass()

内置函数issubclass()用于判断类对象与类对象之间的关系。

内置函数isinstance()用于判断实例对象与类对象之间的关系。

内置函数issubclass接收两个实参:

  1. 类对象。
  2. 类对象或由类对象组成的元组。

当第二个实参是类对象时,如果第一个实参是第二个实参的子类,则返回True。

当第二个实参是类对象组成的元组时,如果第一个实参是第二个实参中任意一个类对象的子类,返回True。

class A(object):
    pass

class B(object):
    pass

class C(object):
    pass

class D(A):
    pass

issubclass(D, A)
[out]: True

issubclass(D, B)
[out]: False

issubclass(D, (B, A, C))
[out]: True

issubclass(bool, int)
[out]: True

issubclass(bool, str)
[out]: False

issubclass(bool, (str, int))
[out]: True

isinstance()

内置函数isinstance接收两个实参:

  1. 实例对象。
  2. 类对象或由类对象组成的元组。

当第二个实参是类对象时,如果第一个实参是第二个实参的实例对象,或者第一个实参是第二个实参的子类的实例对象,那么返回True。

当第二个实参是类对象组成的元组时,如果第一个实参是第二个实参中任意一个类对象或其子类的实例对象,那么返回True。

isinstance(D(), D)
[out]: True

isinstance(D(), A)    # 子类的实例也算
[out]: True

isinstance(D(), (D, B, C))
[out]: True

15.type() & dir()

dir()

内置函数type()用于获得指定对象的类型。

实例对象的类型就是其对应的类对象。 、

类对象的type就是type

class MyClass(object):
    pass

mc = MyClass()
type(mc)
[out]: __main__.MyClass

type(type)
[out]: type

type(int)
type(MyClass)
[out]: type
type

自定义函数的类型是function。

内置函数的类型是 builtin_function_or_method。

def do_sth():
    pass

type(do_sth)
[out]: function

type(dir)
[out]: builtin_function_or_method

type(dir())
[out]: list

可以使用运算符==判断某个对象的类型是都是指定的类型。

对于基本数据类型可以直接使用其对应的类名;如果不是基本数据类型,需要使用标准库中的模块types。

type(18) == int
[out]: True

type(do_sth) == function
[out]: 
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-3-3d5266715006> in <module>
----> 1 type(do_sth) == function

NameError: name 'function' is not defined

import types
type(do_sth) ==types.FunctionType
[out]: True

type(print) == types.BuiltinFunctionType
[out]: True

dir()

对于制定的类对象或实例对象,可以调用内置函数dir()获得其所有可以访问的属性和方法(包括从父类中继承的属性和方法)

类对象与实例对象的结果是有区别的,类对象的结果中不包括实例属性。

class MyClass(object):
    ca = 'ca'
    def __init__(self):
        self.ia = 'ia'
    def im(self):
        pass
    @classmethod
    def cm(cls):
        pass
    @staticmethod
    def sm():
        pass


print(dir(MyClass))  # 带双下划线的都是特殊方法
[out]: 
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'ca', 'cm', 'im', 'sm']

dir(MyClass())
[out]: 
['__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 'ca',
 'cm',
 'ia',
 'im',
 'sm']

调用内置函数dir()后的返回值中,很多属性和方法都是以双下划线__开头和结尾的,这些属性和方法中的绝大多数都继承自类object。

以双下划线__开头和结尾的属性被称为特殊属性,以双下划线__开头和结尾的方法被称为特殊方法。

特殊属性和特殊方法都是系统预定义的,我们自定义的属性名和方法名不要以双下划线__开头和结尾。

在我们自定义类对象时,经常会重写一个或多个特殊方法,例如__init__。特殊方法会在特定的情形下被自动调用,很少需要手动调用特殊方法。

print(dir(object))
[out]: 
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

16.属性之增删改查

1.hasattr(object, name)

用于判断指定的对象object中名为name的属性或方法。

2.getattr(object, name[, default])

用于获取指定的对象object中名为name的属性或方法。

如果不指定参数default,那么当object中不存在名为name的属性或方法时,抛出AttributeError。

如果指定了参数default,那么当object中不存在名为name的属性或方法时,就会返回default。

getattr(object, name)等价于object.name

3.setattr(object, name, value)

用于在指定的对象object中添加或修改名为参数name的属性或方法,添加或修改后的值为value。

setattr(object, name, value)等价于object.name = value

4.delattr(object, name)

用于删除指定的对象object中名为参数name的属性或方法。

delattr(object, name)等价于del object.name

注:只有在不知道对象信息的情况下,才会去获取对象的信息。因此,如果可以直接写object.name,就不要写getattr(object, name)。(1、4更为常用)

class MyClass(object):
    def __init__(self):
        self.x = 1
        
    def do_sth(self):
        print('a')


mc = MyClass()
hasattr(mc, 'x')
[out]: True

hasattr(mc, 'do_sth')
[out]: True

hasattr(mc, 'y')
[out]: False

getattr(mc, 'y', 2)
[out]: 2

getattr(mc, 'y', '查找的属性或方法不存在')
[out]: '查找的属性或方法不存在'
 
f = setattr(mc, 'z', 3)
delattr(mc, 'z')
hasattr(mc, 'z')
[out]: False

17.算术运算符重载

标准算术运算符在默认情况下不能用于自定义类对象的实例对象。

如果想让标准算术运算符可以用于自定义类对象的实例对象,

必须在自定义类对象中实现标准算术运算符对应的以下特殊方法:

  1. +对应的额数方法是__add__()__radd()__();
  2. -对应的额数方法是__sub__()__rsub()__();
  3. *对应的额数方法是__mul__()__rmul()__();
  4. /对应的额数方法是__truediv__()__rtruediv()__();
  5. //对应的额数方法是__floordiv__()__rfloordiv()__();

假设两个运算数obj1和obj2,以+为例,对于obj1 + obj2,需要在obj1对应的自定义类对象中实现特殊方法__add__(), 或在obj2对应的自定义类对象中实现特殊方法__radd__()(radd中的r时right的缩写,因为obj2位于运算符+的右边,所以实现的特殊方法是__radd__();因为obj1位于运算符+的左边,所以实现的特殊方法是__add__()

class MyClass1(object):
    pass

class MyClass2(object):
    pass


MyClass1() + MyClass2()
[out]: 
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-49-4d6a07e51bd1> in <module>
----> 1 MyClass1() + MyClass2()

TypeError: unsupported operand type(s) for +: 'MyClass1' and 'MyClass2'


class MyClass1(object):
    def __add__(self, other):
        return '__add__'

class MyClass2(object):
    def __radd__(self, other):
        return '__radd__'


obj1 = MyClass1()
obj2 = MyClass2()
obj1+obj2
[out]: '__add__'


class MyClass1(object):
    def __add__(self, other):
        print('__add__')
        return NotImplemented

class MyClass2(object):
    def __radd__(self, other):
        return '__radd__'


obj1 = MyClass1()
obj2 = MyClass2()
obj1+obj2
[out]: __add__
'__radd__'


class MyClass1(object):
    def __add__(self, other):
        print('__add__')
        return NotImplemented

class MyClass2(object):
    def __radd__(self, other):
        print( '__radd__')
        return NotImplemented


obj1 = MyClass1()
obj2 = MyClass2()
obj1+obj2
[out]: __add__
__radd__

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-59-794a64de82d0> in <module>
      1 obj1 = MyClass1()
      2 obj2 = MyClass2()
----> 3 obj1+obj2

TypeError: unsupported operand type(s) for +: 'MyClass1' and 'MyClass2'

18.__str__() & __repr__()

类中定义之后即可调用这两个内置函数__str__()__repr__()

类对象的特殊方法之__str__()__repr__()用于自定义并返回实例对象的字符串表示形式。

#一
class MyClass(object):
    pass


mc = MyClass()
mc
[out]: <__main__.MyClass at 0x2403ead4cc0>

print(mc)  # 所属类和内存地址
[out]: <__main__.MyClass object at 0x000002403EAD4CC0>

str(mc)
[out]: '<__main__.MyClass object at 0x000002403EAD4CC0>'

repr(mc)
[out]: '<__main__.MyClass object at 0x000002403EAD4CC0>'

#二
class MyClass(object):
    def __str__(self):
        return 'str__'


mc = MyClass()
mc
[out]: <__main__.MyClass at 0x1fbfffd7cc0>

print(mc)
[out]: str__

str(mc)
[out]: 'str__'

repr(mc)
[out]: '<__main__.MyClass object at 0x000001FBFFFD7CC0>'

#三
class MyClass1(object):
    def __str__(self):
        return 'str__'
    
    def __repr__(self):
        return 'repr__'
   
	
mc1 = MyClass1()
mc1
[out]: repr__

print(mc1)
[out]: str__

str(mc1)
[out]: 'str__'

repr(mc1)
[out]: 'repr__'

#四
class MyClass2(object):
    def __repr__(self):
        return 'repr__'
    
mc2 = MyClass2()
mc2
[out]: repr__

print(mc2)
[out]: repr__

str(mc2)
[out]: 'repr__'

repr(mc2)
[out]: 'repr__'
  1. 当在交互式命令行中直接打印一个实例对象时。 如果在实例对象对应的类对象中实现了特殊方法__repr__(),会自动调用该方法; 否则,会打印实例对向对应的类对象和实例对象在内存中的地址。
  2. 当调用内置函数print打印一个实例对象时, 如果在实例对象对应的类对象中实现了特殊方法__str__(),会自动调用该方法; 否则,如果在实例对象对应的类对象中实现了特殊方法__repr__(), 会自动调用该方法; 否则,会打印实例对象对应的类对象和实例对象在内存中的地址。
  3. 当调用内置函数str创建字符串并且实参是一个实例对象时,
    如果在实例对象对应的类对象中实现了特殊方法__str__(),在内置函数str的内部会自动调用该方法;
    否则,如果在实例对象对应的类对象中实现了特殊方法__repr__(),在内置函数str的内部会自动调用该方法;
    否则,会打印实例对象对应的类对象和实例对象在内存中的地址。
  4. 当调用内置函数repr创建字符串并且实参是一个实例对象时,
    如果在市里对昂对应的类对象中实现特殊方法__repr__(),在内置函数repr的内部会自动调用该方法;
    否则,会打印实例对象对应的类对象和实例对象在内存中的地址。

通常情况下,类对象的特殊方法__str__()__repr__()的实现代码是一样的,因此,当实现了其中一个后,可以把其方法名赋值给另一个的方法名。

class MyClass(object):
    def __str__(self):
        return "实例对象的字符串"
    
    __repr__ = __str__

内置函数str()repr()都返回对象的字符串表示,其区别在于:
str()的返回值是给用户看的,更加用户友好;
repr()的返回值是给程序开发者看的,是为调试服务的。

str('Hello, \nworld')
[out]: 'Hello, \nworld'

repr('Hello, \nworld')
[out]: "'Hello, \\nworld'"

转载请注明:南梦婷的博客 » 点击阅读原文


Similar Posts

Comments