Python面向对象class魔术函数总结

面向对象class的魔术函数

slots防止用户意外添加一些不想要的属性,限制当前类能添加的属性,不在slots会报错

import traceback
from types import MethodType

class MyClass(object):
    __slots__ = ['name', 'set_name']  # 限制能添加的属性,只能添加name set_name

def set_name(self, name):
    self.name = name

cls = MyClass()
cls.name = 'Tom'
cls.set_name = MethodType(set_name, cls)
cls.set_name('Jerry')
print(cls.name)
try:
    cls.age = 30  # 添加age属性非法不在slot里,报AttributeError
except AttributeError:
    traceback.print_exc()

# 但slots的属性限制功能只对当前class起作用,对子类无效
class ExtMyClass(MyClass):
    pass

ext_cls = ExtMyClass()
ext_cls.age = 30 # 子类不受父类的slots限制,不会报错
print(ext_cls.age)

改变默认class返回classObject地址的行为,使其可以像str一样直接打印出来,实现str方法

class MyClass:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        print('print will call __str__ first.')
        return 'Hello ' + self.name + '!'

print(MyClass('Tom'))

实现一个类,支持next()迭代(即是Iterator,则一定是iterable的可用for遍历),需要实现2个方法:iter, next

class Fib100:
    def __init__(self):
        self._1, self._2 = 0, 1

    def __iter__(self): # 通常直接返回self
        return self
    
    def __next__(self):  # 关键在于next方法,在其中做真正的迭代运算
        self._1, self._2 = self._2, self._1 + self._2
        if self._1 > 100:
            raise StopIteration() # 达到迭代结束条件,抛出StopIteration,系统会自动识别该异常
        return self._1

for i in Fib100():
    print(i)

自己实现一个class支持下标访问,就要实现__getitem__方法

class Fib:
    def __getitem__(self, n):
        # print(type(n))
        a, b = 1, 1
        for i in range(n):
            a, b = b, a + b
        return a

f = Fib()
print(f[1])
print(f[5])
print(f[10])
# print(f[1:2:3])

自定义class支持callable行为,等同c++中functor函数对象,必须实现call方法

class MyClass:
    def __call__(self):
        print('You can call cls() directly.')

cls = MyClass()
cls()

print(callable(cls))
print(callable(max))
print(callable([1, 2, 3]))
print(callable(None))
print(callable('str'))

借助Enum为了提高可读性

from enum import Enum

Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr'))
for name, member in Month.__members__.items():
    print(name, '=>', member, ',', member.value)

jan = Month.Jan
print(jan)

用type 利用已有现成的方法,动态构造一个class

# 假设已经有现成的 init, say_hello方法,来构造新的类,且避免重复代码
def init(self, name):
    self.name = name

def say_hello(self):
    print('Hello, %s!' % self.name)


# 注意这种方式会报错,Python语法限制。(object)使得解释器无法区分是tuple还是单个元素,会被解析为单个元素
Hello = type('Hello', (object), {'__init__':init, 'hello':say_hello})

# tuple要这样(object, )
Hello = type('Hello', (object, ), {'__init__':init, 'hello':say_hello})
# Hello = type('Hello', (object, ), dict(__init__ = init, hello = say_hello))


''' 上述 Hello 的定义等效于下面的写法
class Hello:
    def __init__(...)
    def hello(...)
'''
h = Hello('Tom')
h.hello()

Python这种动态添加函数,构造class过程,其实是维护了一个class属性信息的表,可以灵活动态增加(怎么感觉有点类似c++的vptr??)

metaclass元类

def add(self, value):
    self.append(value)

# metaclass元类一定是从type继承的,可用于控制class的创建过程,动态增加class的attrs表
metaclass控制类的创建
class ListMetaclass(type):
    def __new__(cls, name, bases, attrs):
        print(cls)
        print(name)
        print(bases)
        print(type(attrs))

        # attrs添加可以是lambda函数也可以是普通函数
        # attrs['add'] = lambda self, value: self.append(value)
        attrs['add'] = add
        attrs['name'] = 'Tom'
        return type.__new__(cls, name, bases, attrs) #必须要return type.new

class MyList(list, metaclass = ListMetaclass):  # 额外增加add方法,实际等价于append。
    pass

mli = MyList()
mli.add(1)
mli.add(2)
mli.add(3)
print(mli.name)
print(mli)


metaclass用于控制其他class的创建,最常用法就是对attrs修改,继承一个类型,比如上例中list

try-except-else-finally异常捕获

import traceback

try:
    # r = 10 / 0
    r = 10 / 1
except ZeroDivisionError as e:
    print(e)
    r = 1
else:
    print('没有异常')
finally:
    print('不管有没有异常都执行')
print(r)

使用unittest进行单元测试用例case, 并不需要实例化测试类

import unittest

class MyDict(dict):
    pass

class TestMyDict(unittest.TestCase):
    def setUp(self):
        print('测试前准备')

    def tearDown(self):
        print('测试后清理')
    
    def test_init(self):
        md = MyDict(one = 1, two = 2)
        self.assertEqual(md['one'], 1)
        self.assertEqual(md['two'], 2)
        # self.assertEqual(md['two'], 3)
    
    def test_nothing(self):
        pass

if __name__ == '__main__':
    unittest.main()

# python test_module.py
# python -m unittest test_module
# python -m unittest test_module.test_class
# python -m unittest test_module.test_class.test_method

property描述器、装饰器

import traceback

class Student:
    @property
    def score(self):
        return self._score

    @score.setter  # 装饰器
    def score(self, value):
        if not isinstance(value, int):
            raise ValueError('not int')
    
        elif (value < 0) or (value > 100):
            raise ValueError('not between 0 ~ 100')
    
        self._score = value
    
    @property
    def double_score(self):
        return self._score * 2

s = Student()
s.score = 75
print(s.score)
try:
    s.score = 'abc'
except ValueError:
    traceback.print_exc()
try:
    s.score = 101
except:
    traceback.print_exc()
print(s.double_score)
try:
    s.double_score = 150
except AttributeError:
    traceback.print_exc()



class MyProperty:
    def __init__(self, fget = None, fset = None, fdel = None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel

    def __get__(self, instance, cls):
        if self.fget:
            print('__get__')
            return self.fget(instance)
    
    def __set__(self, instance, value):
        if self.fset:
            print('__set__')
            return self.fset(instance, value)
    
    def __delete__(self, instance):
        if self.fdel:
            return self.fdel(instance)
    
    def getter(self, fn):
        self.fget = fn
        
    def setter(self, fn):
        self.fset = fn
    
    def deler(self, fn):
        self.fdel = fn

class Student:
    @MyProperty
    def score(self):
        return self._score

    @score.setter
    def set_score(self, value):
        self._score = value

s = Student()
s.score = 95
print(s.score)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值