首页 > 其他 > 详细

面向对象进阶

时间:2019-08-06 17:14:43      阅读:77      评论:0      收藏:0      [点我收藏+]
面向对象进阶

一、isinstance(obj,cls)与issubclass(sub,super)

isinstance(obj,cls)检查是否obj是否是类cla的对象。具体代码如下所示:

class Foo:
    pass
f1 = Foo()
print(isinstance(f1,Foo))   #结果为:True

issubclass(sub,super)检查sub类是否是super类的派生类。具体代码如下:

class Foo:
    pass
class Bar(Foo):
    pass
print(issubclass(Bar,Foo))   #结果为:True

二、__getattribute__

我们在前面介绍过__getattr__的用法,当只有在使用点调用属性且属性不存在时候才会执行,那么__getattribute__不管存不存在都会执行,具体实现代码如下:

class Foo:
    def __init__(self,x):
        self.x = x
    def __getattribute__(self, item):
        print(执行的是getattribute)
f1 = Foo(10)
f1.x      #结果为:执行的是getattribute
f1.sss     #结果为:执行的是getattribute

那么__getattr__与__getattribute__一起使用时,会先执行谁呢?我们通过一段代码来分析:

class Foo:
    def __init__(self,x):
        self.x = x
    def __getattr__(self, item):
        print(执行的是getattr)
    def __getattribute__(self, item):
        print(执行的是getattribute)
f1 = Foo(10)
f1.x   #结果为:执行的是getattribute
f1.sss    #结果为:执行的是getattribute

从结果可以看出当__getattr__与__getattribute__一起使用时,只会执行__getattribute__。如果要执行__getattr__,除非__getattribute__在执行过程中抛出异常AttributeError。

三、类内置item函数

类内置item函数与之前学过的类内置attr函数用法十分相似,类内置attr函数是以点的方式调用,而类内置item函数以字典的方式调用

类内置item函数,分别是__getitem__、__setitem__、__delitem__。具体实现代码如下:

class Foo:
    def __getitem__(self, item):
        print(getitem)
        return self.__dict__[item]
    def __setitem__(self, key, value):
        print(setitem)
        self.__dict__[key] = value
    def __delitem__(self, key):
        print(delitem)
        self.__dict__.pop(key)
f1 = Foo()
print(f1.__dict__)      #结果为:{}

f1[name] = alex     #结果为:setitem
f1[age] = 18        #结果为:setitem
print(f1.__dict__)      #结果为:{‘name‘: ‘alex‘, ‘age‘: 18}

del f1[name]        #结果为:delitem
print(f1.__dict__)      #结果为:{‘age‘: 18}

f1[age]      #结果为:getitem

四、__str__与__repr__

这两个方法是改变对象的字符串显示,从字面意思就可以看出把输出的对象用于字符串显示。

我们来对以下代码来分析:

class Foo:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def __str__(self):
        return 名字是%s年龄是%s %(self.name,self.age)
    def __repr__(self):
        return 这是repr
f1 = Foo(alex,18)
print(f1)     #结果为:名字是alex年龄是18

上述代码使用print操作时执行了类中的__str__方法,那么将__str__方法先注释,我们来看看到底print打印了什么?

class Foo:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def __repr__(self):
        return 这是repr
f1 = Foo(alex,18)
print(f1)    #结果为:这是repr‘

可以看出print操作执行了类中的__repr__方法,那么当__str__与__repr__一起使用时,一定是执行__str__吗?答案不是。下面就来介绍当什么情况的时候执行谁?

  • 当str函数或者print函数时,执行的是__str__;
  • 当repr函数或者交互式解释器,执行的是__repr__;
  • 如果__str__没有被定义,那么就会使用__repr__来替换输出;
  • 这两方法的返回值必须是字符串,否则抛出异常

五、__format__

__format__可以自定制格式化字符串。具体实现如下:

format_dic = {
    ymd : {0.year}{0.mon}{0.day},
    y:m:d : {0.year}:{0.mon}:{0.day},
    y-m-d : {0.year}-{0.mon}-{0.day}
}
class Date():
    def __init__(self,year,mon,day):
        self.year = year
        self.mon = mon
        self.day = day
    def __format__(self, format_spec):
        if not format_spec or format_spec not in format_dic:  #不写格式或者不符合字典的格式,即生成默认格式
            format_spec = ymd
        fm = format_dic[format_spec]
        return fm.format(self)
d1 = Date(2019,7,30)
print(format(d1))
print(format(d1,ymd))
print(format(d1,y-m-d))
print(format(d1,dgdg))

执行结果为:

2019730
2019730
2019-7-30
2019730

六、__slots__

__slots__是一个类变量,变量值可以是列表,元组,或者可迭代对象,也可以是一个字符串。

为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,单身有很多实例,为了节省内存可以使用__slots__取代实例的__dict__。

当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个。

class Foo:
    __slots__ = name

f1 = Foo()
f1.name = alex
print(f1.name)    #结果为:alex
#print(f1.__dict__)   #报错,因为类中定义了__slots__=‘name‘,所以name并没有存在属性字典中
print(f1.__slots__)    #结果为:name

七、__doc__

__doc__是类的描述信息,该属性不能被继承。代码如下:

class Foo:
    我是描述信息
    pass

class Bar(Foo):
    pass

print(Foo.__doc__)   #结果为:我是描述信息
print(Bar.__doc__)   #结果为:None

八、__module__与__class__

__module__ 表示当前操作的对象在那个模块。

__class__表示当前操作的对象的类是什么。

例如aa.py文件下写如下代码:

class C:
    def __init__(self):
        self.name = alex

在另一个文件下执行下面代码:

from aa import C
print(c1.__module__)   #结果为:aa
print(c1.__class__)    #结果为:<class ‘aa.C‘>

九、__del__

__del__被称为析构方法,当对象在内存中被释放时,自动触发执行。下面我们对两段代码进行分析:

第一段代码:

class Foo():
    def __init__(self,name):
        self.name = name
    def __del__(self):
        print(我执行了)
f1 = Foo(alex)
print(-------------->)

执行结果为:

-------------->
我执行了

第二段代码:

class Foo():
    def __init__(self,name):
        self.name = name
    def __del__(self):
        print(我执行了)
f1 = Foo(alex)
del f1
print(-------------->)

执行结果为:

我执行了
-------------->

上面两段代码为何输出结果相反?因为执行del了相当于提前释放对象。

十、__call__

__call__是指对象后面加括号,触发执行。

我们以前学过构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()。具体代码如下:

class Foo:
    def __call__(self, *args, **kwargs):
        print(实例执行了)

f1 = Foo()
f1()  #结果为:实例执行了

十一、__iter__与__next__

这两个方法跟我们之前学的迭代器方法是一样的,它们就是迭代器协议。实现代码如下:

class Foo:
    def __init__(self,n):
        self.n = n
    def __iter__(self):
        return self
    def __next__(self):
        if self.n == 13:    #当self.n等于13时抛出异常
            raise StopIteration(终止了)
        self.n += 1
        return self.n

f1 = Foo(10)
print(f1.__next__())   #结果为:11
print(f1.__next__())   #结果为:12
print(next(f1))    #结果为:13
print(next(f1))    #抛出异常

利用迭代器协议实现斐波那契数列,具体实现代码如下:

class Fib:
    def __init__(self):
        self._a = 1
        self._b = 1
    def __iter__(self):
        return self
    def __next__(self):
        if self._a > 10:
            raise StopIteration(终止)
        self._a,self._b = self._b,self._a+self._b
        return self._a
f1 = Fib()
print(next(f1))
print(next(f1))
print(-------------->)
for i in f1:
    print(i)

执行结果为:

1
2
-------------->
3
5
8
13

 

面向对象进阶

原文:https://www.cnblogs.com/lzc69/p/11310279.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!