python魔术方法学习

实例方法
1、在定义实例时,会初始化一个实例对象,其第一个参数一般为self,在创建时候将其传给self,
2、实例的方法和属性只能由实例调用

类方法
1、在类定义中,必须用@classmethod装饰器修饰的方法
2、必须至少有一个参数,第一个参数留给了cls,cls指待调用者即类本身
3、cls这个标识符可以是任意合法名称,但是为了易读性,一般写成cls
4、通过cls直接操作类的属性即方法

静态方法
1、在类定义中,必须用@staticmethod装饰器修饰的方法
2、在调用时不用隐形的传入参数,静态方法,只是表明这个方法属于这个名词空间,函数归在一起,方便管理

总结:
类除了普通方法都可以调用,普通方法需要对象的实例作为第一个参数
实例可以调用类中定义的所有的方法和属性,包括静态方法和类方法,普通方法传入实例对象,类方法和静态方法需要找到实例的类

访问控制
私有(private)属性
使用双下划线开头的属性就属于私有属性
私有变量的本质:类定义的时候,如果声明一个实例变量的时候,使用双下划线,python解释器会将其改名,转化名称为_类名__变量名的名称,所以用原来名字就访问不到了。只要使用新变量的名称,还可以在类以外进行访问和更改

保护变量
在变量名前加一个下划线,称之为保护变量。可以将其理解为君子协议,告诉开发者要将其当做私有变量

私有方法
单下划线的定义只是开发者之间的约定,python解释器不做任何解释
双下划线的方法,称之为私有方法,定义改名与私有属性一致,其方法和属性都可以在类的__dict__中找到

私有成员的总结
在python中使用_单下划线或者__双下划线来标识一个成员被保护或者被私有化隐藏起来
但是不管使用什么样的访问控制,都不能真正的阻止用户修改类的成员,python中没有绝对的安全的保护成员或者私有成员,因此,前导的下划线只是一种提醒或者警告,请遵守这个约定,除非真有必要,不要修改或者使用保护成员或者私有成员,更不要修改他们。

python中魔术方法

1、实例化
__new__     意义:实例化一个对象,该方法需要返回一个返回值,

class A:
    # @staticmethod
    def __new__(cls, *args, **kwargs):  # 静态方法
        cls.test = 'abc'  # 给类增加属性,但是不好,每次实例化都要创建一次
        # return 'abc'
        print(cls)
        print(args)  # ('tom',)
        # args = ('jerry', )  # 不会改变a.name的值
        print(kwargs)  # {'name': 'tom'}
        ret = super().__new__(cls)
        # ret.age = 100  # 不建议在这里,可以放在__init__方法中
        return ret  # 调用父类的方法;返回实例

    def __init__(self, name):  # 将__new__方法返回的实例注入到self
        self.name = name
        
#a = A('tom')
a = A(name='tom')
print(a)  # None
print(a.name)
print(A.__dict__)
#{'__module__': '__main__', '__new__': <staticmethod object at 0x00000000027D9A58>, '__init__': <function A.__init__ at 0x00000000027D3F28>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None, 'test': 'abc'}
<Cart Cart [1, 2, 1, 2]>
print(a.age)
__new__方法很少使用,即使创建了该方法,也会使用return super().new(cls)基类object的__new__方法来创建实例并返回。

2、可视化
在这里插入图片描述

class Person:
    def __init__(self, name, age=18):
        self.name = name
        self.age = age

    def __str__(self):
        return 'str: {} {}'.format(self.name, self.age)

    def __repr__(self):
        return 'repr: {} {}'.format(self.name, self.age)

    def __bytes__(self):
        return 'bytes: {} {}'.format(self.name, self.age).encode()


a = Person(name='tom')
print(a)  # <__main__.Person object at 0x00000000027F0FD0>
#str: tom 18
print(str(a))  # <__main__.Person object at 0x00000000027F0FD0>
print(str(a).encode())  # b'str: tom 18'

print(bytes(a))  # b'bytes: tom 18'
print(repr(a))  # repr: tom 18

print([a])  # [repr: tom 18]  print(str([])) 列表中又调用repr
print((a, ))  # (repr: tom 18,)
print('{}'.format(a))  # str: tom 18
没有str方法,会找repr方法,但是没有repr方法,直接找基类
注意不能通过判断是否带引号来判断输出值的类型,类型判断要使用type或isinstance

4.hash
__hash__方法,内建函数hash() 调用的返回值,返回一个整数。如果定义这个方法该类的实例就可hash。(整数的hash算法是取模)

class Person:
    def __init__(self, name, age=18):
        self.name = name
        self.age = age

    def __hash__(self):
        return 1

    def __eq__(self, other):
        return self.age == other.age  # 这样写的话,p1 == p2 ——>True

    def __repr__(self):
        return '<Person {} {}>'.format(self.name, self.age)

print(hash(1), hash(2), hash(5000000))  # 整数的hash算法是取模,除数是62位的整数
print(hash('abc'))
print(hash(('abc', )))
print(hash(Person))
print(hash(Person('tom')))
p1 = Person('tom')
p2 = Person('jerry')
print(hash(p1), hash(p2))
print({123, 123})  # {123}去重了
print({p1, p2})  # {<Person tom 18>, <Person jerry 18>}
 没有去重,是因为p1和p2的内容不同(虽然他们的hash值相同);去重两个条件内容相同,hash值相同
print('~~~~~~~~~~', p1 == p2)  # 会调用__eq__方法
print({(123, ), (123, )})  # {(123,)} 去重了

set去重需要两个条件: 内容相同,hash值相同

在这里插入图片描述
__hash__方法只是返回一个hash值作为set的key,但是去重,还需要__eq__方法来判断两个对象是否相等,hash值相等,只是hash冲突,不能说明两个对象是相等的。
因此,一般来说提供__hash__方法是为了作为set或者dict的key,如果去重,要同时提供__eq__方法。不可hash对象isinstance(p1, collections.Hashable)一定为False

5、bool
在这里插入图片描述

class A:
    pass
print(bool(A))  # True
print(bool(A()))  # True
print(bool([]))  # False

class B:
    def __bool__(self):
        print('in bool')
        # return 1
        # return bool(self)  # 无限递归
        return bool(1)

print(bool(B))  # True
#print(bool(B()))  # 会出错
if B():
    print('b~~~~~~~~~~~~')

class C:
    def __len__(self):
        return 1  # 必须大于0

print(bool(C))
print(bool(C()))

6.运算符重载
在这里插入图片描述
运算符重载

class A:
    def __init__(self, age):
        self.age = age

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

    def __isub__(self, other):
        # return A(self.age - other.age)  #新实例
        # self.age -= other.age
        # return self  # 31691272 <__main__.A object at 0x0000000001E39208>\
        # 31691272 <__main__.A object at 0x0000000001E39208>就地修改
        return A(self - other)  # 新实例


a1 = A(20)
a2 = A(12)
print(id(a1), a1)  # 32150024 <__main__.A object at 0x0000000001EA9208>
#print(a1 - a2)  # 8数值
#print(a1.__sub__(a2))  # 8
#print(a2 - a1, a2.__sub__(a1))  # -8
a1 -= a2  # a1__isub__(a2)
print(id(a1), a1)  # 32151032 <__main__.A object at 0x0000000001EA95F8>

__isub__方法定义,一般会in-place就地修改自身;如果没有定义__isub__方法,则会调用__sub__方法

7.容器相关方法
在这里插入图片描述
使用上下文管理器计算函数执行时间
with声明是从Python2.5开始引进的关键词。你应该遇过这样子的代码:
with open(‘foo.txt’) as bar:
# do something with bar
在with声明的代码段中,我们可以做一些对象的开始操作和清除操作,还能对异常进行处理。
这需要实现两个魔术方法: enterexit

enter(self)

__enter__会返回一个值,并赋值给as关键词之后的变量。在这里,你可以定义代码段开始的一些操作。

exit(self, exception_type, exception_value, traceback)

__exit__定义了代码段结束后的一些操作,可以这里执行一些清除操作,或者做一些代码段结束后需要立即执行的命令,比如文件的关闭,socket断开等。如果代码段成功结束,那么exception_type, exception_value, traceback 三个参数传进来时都将为None。如果代码段抛出异常,那么传进来的三个参数将分别为: 异常的类型,异常的值,异常的追踪栈。
如果__exit__返回True, 那么with声明下的代码段的一切异常将会被屏蔽。
如果__exit__返回None, 那么如果有异常,异常将正常抛出,这时候with的作用将不会显现出来。

class Timer:
    def __init__(self, fn):
        self._fn = fn

    def __call__(self, *args, **kwargs):
        start = time.time()
        self._fn(*args, **kwargs)
        end = time.time()
        return end - start

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('exit')

import time

def add(x, y):
    time.sleep(1)
    return x + y

with Timer(add) as f:
    print(f(4, 5))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值