Python的面向对象(二):Python中神奇的魔法方法

魔法方法

Python里面有一种特殊的方法,叫做魔法方法。Python的类里提供的,是由两个下划线开始,再以两个下划线结束的方法。
魔法方法不需要手动调用,会在恰当的时候就会被激活,自动执行。
魔法方法的名字都是系统规定好的,不能乱写,在合适的时候自己调用。

1.__init__方法

_init_()方法,在创建一个对象时默认被调用,不需要手动调用。在开发中,如果希望在创建对象的同时,就设置对象的属性,可以对__init__ 方法进行改造。

class Student(object):
    def __init__(self, name):  # 重写了___init__魔法方法,设置对象的属性
        # 在创建对象时,会自动调用这个方法。
        print('__init__方法被调用了')
        self.name = name
    def play_game(self):
        print('{}正在玩游戏'.format(self.name))
s1 = Student('张三') #创建对象时,必须要指定name属性的值
print(s1.name)  # 张三
s1.play_game()  # 张三正在玩游戏

运行结果:

__init__方法被调用了
张三
张三正在玩游戏

注意

  1. _init_()方法在创建对象时默认被调用,不需要手动调用。
  2. _init_()方法里的self参数,在创建对象时不需要传递参数,python解释器会把创建好的对象引用直接赋值给self
  3. 在类的内部,可以使用self来使用属性和调用方法;在类的外部,需要使用对象名来使用属性和调用方法。
  4. 如果有多个对象,每个对象的属性是各自保存的,都有各自独立的地址。
  5. 方法是所有对象共享的,只占用一份内存空间,方法被调用时会通过self来判断是哪个对象调用了实例方法。

2.__del__方法

既然有创建对象时的魔法方法调用,就会有删除对象时的魔法方法调用,_del_()方法就是当对象被销毁时,会自动调用这个方法。

class Student(object):
    def __init__(self, name):
        print('__init__方法被调用了')
        self.name = name
    def __del__(self):
        # 当对象被销毁时,会自动调用这个方法
        print('__del__ 方法被调用了')
    def play_game(self):
        print('{}正在玩游戏'.format(self.name))
s1 = Student('张三')
del s1

运行结果:

__init__方法被调用了
del 方法被调用了

3.__str__方法

__str__方法返回对象的描述信息,使用print()函数打印对象时,其实调用的就是这个对象的__str__方法。
调用对象的 __str__方法,默认会打印类名和对象的地址名。

class Student(object):
    def __init__(self, name):
        self.name = name
s1 = Student('张三')
print(s1)   # <__main__.Student object at 0x00000298D1161808>

那我们如何得到s1的值呢?其实想要修改对象的输出结果,我们就需要重写__str__方法。

class Student(object):
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return '姓名:{}'.format(self.name)
s1 = Student('张三')
print(s1)  # 姓名:张三

4.__repr__方法

__repr__方法和__str__方法功能类似,都是用来修改一个对象的默认打印内容。在打印一个对象时,如果没有重写__str__方法,它会自动来查找__repr__方法。如果这两个方法都没有,会直接打印这个对象的内存地址。

class Student(object):
    def __init__(self, name):
        self.name = name
    def __repr__(self):
        return '姓名:{}'.format(self.name)
s1 = Student('张三')
print(s1)  # 姓名:张三

倘若__repr__方法和__str__方法同时出现,会发生什么情况呢???

class Student(object):
    def __init__(self, name):
        self.name = name
    def __repr__(self):
        return 'hello world'
    def __str__(self):
        return '姓名:{}'.format(self.name)
s1 = Student('张三')
print(s1)  # 姓名:张三

从上面的实例可知,若两种方法同时出现,会选择调用__str__方法。
如果还想在有__str__方法的情况下,调用__repr__方法,我们可以调用内置函数repr或者手动调用__repr__方法。

class Student(object):
    def __init__(self, name):
        self.name = name
    def __repr__(self):
        return 'hello world'
    def __str__(self):
        return '姓名:{}'.format(self.name)
s1 = Student('张三')
print(s1)  #自动调用__str__方法
print(repr(s1))  # 调用内置函数 repr 会触发对象的 __repr__ 方法
print(s1.__repr__())  # 手动调用__repr__魔法方法

运行结果:

姓名:张三
hello world
hello world

注意:

  1. 如果不做任何的修改,直接打印一个对象,是文件的 name.类型 内存地址
  2. 当打印一个对象的时候,会调用这个对象的 str 或者 repr 方法
  3. 如果两个方法都写了,选择 str
  4. 如果在两个方法都写了的情况下,调用__repr__方法,我们可以调用内置函数repr或者手动调用__repr__方法。

5.__call__方法

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

class Student(object):
    def __init__(self, name):
        self.name = name
    def __call__(self, *args, **kwargs):
        print('args={},kwargs={}'.format(args, kwargs))
s1 = Student('张三')
s1(1, 2, 3, age=18, score=99)  # args=(1, 2, 3),kwargs={'age': 18, 'score': 99}

6.比较运算符相关的魔法方法

class Student(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score
s1 = Student('zhangsan', 90)
s2 = Student('zhangsan', 90)

s1和s2是否是同一个对象?
比较两个对象是否是同一个对象,我们需要比较的是内存地址

print('0x%X' % id(s1))  # 0x16AA83BB408
print('0x%X' % id(s2))  # 0x16AA83BB448

由上可知,它们的内存地址不一样,两个对象不是同一个对象。

print('s1 is s2', s1 is s2)  # s1 is s2 False
print(s1 == s2)  # False

is 身份运算符,比较两个对象的内存地址
== 会调用对象的 eq 方法,如果不重写,默认比较依然是内存地址,如果重写方法,便可以获取这个方法的比较结果。

class Student(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score
    def __eq__(self, other):
        return self.name == other.name and self.score == other.score
s1 = Student('zhangsan', 90)
s2 = Student('zhangsan', 90)
print( s1 == s2)  # True

其它比较运算符,也是如此。

class Student(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score
        
    def __eq__(self, other):
        return self.name == other.name and self.score == other.score
    #def __ne__(self, other):# 使用 != 运算符会自动调用这个方法
    def __lt__(self, other):
        return self.score < other.score
        
    def __gt__(self, other):
        return self.score > other.score
        
    def __le__(self, other): 
        return self.score <= other.score
        
    def __ge__(self, other): 
        return self.score >= other.score
s1 = Student('zhangsan', 90)
s2 = Student('zhangsan', 90)
s3 = Student('lisi', 94)
s4 = Student('wangwu', 99)
print(s1 == s2)  #True
print(s1 != s2)  #False
print(s1 > s3)   #False
print(s1 >= s4)  #False
print(s1 < s4)   #True
print(s1 <= s3)  #True

7.算数运算符相关的魔法方法

class Student(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score

    def __add__(self, other):
        return self.score + other

    def __sub__(self, other):
        return self.score - other

    def __mul__(self, other):
        return self.name * other

    def __truediv__(self, other):
        return self.score / other

    def __mod__(self, other):
        return self.score % other

    def __pow__(self, power, modulo=None):
        return self.score ** power

s = Student('zhangsan', 95)
print(s + 1)  # 96
print(s - 2)  # 93
print(s * 2)  # zhangsanzhangsan
print(s / 5)  # 19.0
print(s % 5)  # 0
print(s ** 2) # 9025

8.类型转换相关的魔法方法

class Student(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score

    def __int__(self):
        return self.score

    def __float__(self):
        return self.score * 1.0

    def __str__(self):
        return self.name

    def __bool__(self):
        return self.score > 60
        
s1 = Student('zhangsan', 90)
print(int(s1))   # 90
print(float(s1)) # 90.0
print(str(s1))   # zhangsan
print(bool(s1))  # True

9.容器方法

__ len__ -> len    计算长度
__ iter__ -> for    for循环
__ contains__ -> in  判断是否存在
__ getitem__ 对 string、 bytes、list、tuple、dict 有效   下标求值
__ setitem__ 对 list、dict 有效  添加、修改
在这里插入图片描述
需求:求出k列表后三位数

k = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]

print(k.__getitem__(-3:)))  # 报错

这里可以使用slice切片

k = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]

print(k.__getitem__(slice(7, 10)))  # [8, 9, 0]

10.上下文管理with

__ enter__ 进⼊ with 代码块前的准备操作
__ exit__ 退出时的善后操作
⽂件对象、线程锁、socket 对象等都可以使⽤ with 操作。
为什么要使用with来操作???因为使用with可以自动关闭文件,即便是代码程序报错,其也能关闭文件。

class A:
    def __enter__(self):
        print('exec enter')
        return self

    def __exit__(self, a1, a2, a3):
        print('exec exit')
        print('a1:', a1)
        print('a2:', a2)
        print('a3:', a3)

with A() as a:
    print('a = ', a)
    raise ValueError('errorerror')

运行结果:
在这里插入图片描述

11.属性相关的方法

__ dict__、__ setattr__、__ getattribute__、__ getattr__
内建函数:setattr()、getattr()、hasattr()
对象的属性都存放在__ dict__中,类属性在类对象中保存;普通的对象是由类创建的,类是由元类创建的。

class A:
    def __init__(self):
        self.x = 123
        self.y = 456

a = A()
a.z = 789
print(a.__dict__)  # {'x': 123, 'y': 456, 'z': 789}

a.__dict__['x'] = 0

print(a.__dict__)  # {'x': 0, 'y': 456, 'z': 789}
print(a.__dict__['x'])  # 0
class A:
    z = 789

    def __init__(self):
        self.x = 123
        self.y = 456

    def foo(self):
        print(self.x + self.y)

def bar(self):
    return self.x / self.y

A.bar = bar  # 猴子补丁(monkey patch)

print(A.__dict__)

运行结果:

{‘module’: ‘main’,
‘z’: 789,
init’: <function A.init at 0x0000012BA24DA4C8>,
‘foo’: <function A.foo at 0x0000012BA24DA168>,
dict’: <attribute ‘dict’ of ‘A’ objects>,
weakref’: <attribute ‘weakref’ of ‘A’ objects>, # 弱引用
doc’: None, ‘bar’: <function bar at 0x0000012BA24DA438>}

通过 hasattr()可以查看是否拥有属性。

class A:
    z = 789

    def __init__(self):
        self.x = 123
        self.y = 456

a = A()

print(hasattr(a, 'abc'))  # False
print(hasattr(a, 'x'))  # True

通过 setattr()可以设置属性。

class A:
    z = 789

    def __init__(self):
        self.x = 123
        self.y = 456

a = A()

setattr(a, 'x', 777)
print(a.x)  # 777
setattr(a, 'h', 888)
print(a.h)  # 888

通过 getattr()可以获得属性。

class A:
    z = 789

    def __init__(self):
        self.x = 123
        self.y = 456

a = A()

print(getattr(a, 'x'))  # 123
print(getattr(a, 'r', 111))  # 没有可以设置默认值:111

通过 delattr()可以删除属性。

class A:
    z = 789

    def __init__(self):
        self.x = 123
        self.y = 456

a = A()

delattr(a, 'y')
print(a.y)  # 报错

__ setattr__、__ getattribute__、__ getattr__的使用

class User:
    z = [7,8,9]

    def __init__(self):
        self.money = 10000
        self.y = 'abc'

    def __setattr__(self, name, value):
        print('set %s = %s' % (name, value))
        object.__setattr__(self, name, value)

    def __getattribute__(self, name):
        print('getattribute %s' % name)
        return object.__getattribute__(self, name)

    def __getattr__(self, name):
        print('getattr %s' % name)
        return -1

    def foo(self, x, y):
        return x * y

当u = User()时,会执行__ init__()和__ setattr__()
当我们调用foo函数时u.foo(x,y),会执行__ getattribute__()
当我们找不到属性时,会执行__ getattribute__()和__getattr__()

12.槽(__ slots__)

其可以固定类所具有的属性,使实例不会分配__dict__,也无法动态添加属性,但其可以优化内存分配。
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值