Python 学习笔记(10)面向对象特殊成员

Python 学习笔记(10)面向对象特殊成员

10.1 限制添加实例属性及实例方法

类可以动态添加三种类方法:实例方法、类方法以及静态方法;但由类创建出的实例对象只能动态添加实例方法。

类动态添加方法会影响由此类创造出的所有实例对象;而单个实例对象添加实例方法只会对其自身起作用,不会影响其他实例对象。

def printStr_1(self):
    print("实例方法")
    
@classmethod
def printStr_2(cls):
    print("类方法")
    
@staticmethod
def printStr_3():
    print("静态方法")
    
class FirstDemo():
    pass

if __name__ == "__main__":
    FirstDemo.printStr_2 = printStr_2
    FirstDemo.printStr_3 = printStr_3
    
    demo_1 = FirstDemo()
    demo_1.printStr_2()
    demo_1.printStr_3()
    
    demo_2 = FirstDemo()
    demo_2.printStr = printStr_1
    demo_2.printStr(demo_2)
    
Out:
类方法
静态方法
实例方法

动态添加属性及方法为类的使用增加了灵活性,但如此也造成了可以动态修改类的隐患,因此需要对此进行限制。

python中使用**__slots__ 属性**来限制【实例对象】动态添加属性及方法,但无法对【类本身】动态添加属性及方法进行限制。

__slots__ 属性为元组类型,其中元素值为指定可以动态添加的实例属性或实例方法的名称。

def printStr(self):
    print(self.string)

class FirstDemo():
    __slots__ = ('printStr', 'string')
    
if __name__ == "__main__":
    demo = FirstDemo()
    demo.printStr = printStr
    demo.string = "实例方法"
    demo.printStr(demo)
    demo.name = "非法属性"
    
Out:
实例方法
AttributeError: 'FirstDemo' object has no attribute 'name'

__slots__ 属性对于所定义类派生出的子类没有效果,只对当前所在的类产生限制。如果子类中自己定义了**__slots__ 属性**,则子类实例对象属性或方法的限制范围为子类与父类的并集。

10.2 type()函数动态创建类

在python中,type() 的主要作用为查看指定对象的类型,其用法为:

type(obj)

但其实 type() 也可以用来创建类,需要指定类的名称、父类以及其中定义的属性或方法,其用法为:

def printStr(self, string):
    print(string)

if __name__ == "__main__":
    SecondDemo = type('SecondDemo', (object,), dict(printStr=printStr, var='类属性'))
    demo = SecondDemo()
    demo.printStr("实例方法")
    print(SecondDemo.var)

Out:
实例方法
类属性

10.3 MetaClass元类

MetaClass 元类的作用是为类内部定义的属性及方法(包括类属性以及类方法)进行动态的修改。

使用 MetaClass 的步骤为:

1)创建 MetaClass 元类,该类需要显式继承自 type 类,且需要包含一个 __new__ 方法且能够返回该类的实例对象;

2)使用 MetaClass 元类去创建类;

3)使用新创建的类的实例对象实现相应功能。

class MetaClass(type):
    def __new__(cls, name, bases, dicts):
        dicts['var'] = '类属性'
        dicts['printStr'] = lambda self: print(string)
        return super().__new__(cls, name, bases, dicts)
        
class ThirdDemo(metaclass=MetaClass):
	pass

if __name__ == "__main__":
    demo = ThirdDemo()
    demo.printStr("实例方法")
    print(ThirdDemo.var)
   
Out:
实例方法
类属性

10.4 枚举类

枚举类通常用来定义实例化对象个数固定的类(月份),与普通的类的用法不同,枚举类不能创建实例。

枚举类中的属性分为 name value 两部分, name 代表该枚举值的变量名, value 代表该枚举值的序号。

枚举类中必须保证各属性的 name 不同,但 value 可以相同。

from enum import Enum

class Month(Enum):
    January = 1
    February = 2
    March = 3
    April = 4
    May = 5
    June = 6
    July = 7
    Augest = 8
    September = 9
    October = 10
    November = 11
    December = 12
    
if __name__ == "__main__":
    print(Month.January)
    print(Month['January'])
    print(Month(1), '\n')
    
    for month in Month:
        print(month)
    print('\n')
    
    for name, value in Month.__members__.items():
        print(name, value)

Out:
Month.January
Month.January
Month.January 

Month.January
Month.February
Month.March
Month.April
Month.May
Month.June
Month.July
Month.Augest
Month.September
Month.October
Month.November
Month.December


January Month.January
February Month.February
March Month.March
April Month.April
May Month.May
June Month.June
July Month.July
Augest Month.Augest
September Month.September
October Month.October
November Month.November
December Month.December

也可以使用 Enum() 函数创建枚举类:

Month = Enum('Month', ('January', 'February', 'March', 'April', 'May', 'June', 'July', 'Augest', 'September', 'October',
                       'November', 'December'))

10.5 类的特殊成员

在类中所有以双下划线开头和结尾命名的属性和方法都被称为类的特殊成员,这些特殊成员不允许在类的外部直接进行调用,但可以借助类中定义的方法进行调用、修改或者根据需要进行重写。

10.5.1 __new__()

__new__()方法是用于创建【类实例】的一个静态方法,无需使用 @staticmethod 装饰器且会在调用 __init__()初始方法前被调用。

__new__()方法通常会返回该类的一个实例,但有时也会返回其他类的实例,此时会跳过对 __init__() 的调用。

class FifthDemo():
    def __new__(cls, *args, **kwargs):
        return super().__new__(cls, *args, **kwargs)

10.5.2 __repr__()

python中每个类中都包含 __repr__() 方法,通过重写 __repr__() 方法可以用于自定义输出实例化对象的信息。

默认情况下, __repr__() 方法输出的是该类创建的实例对象相关的【类名+ object at + 内存地址】信息。

class FifthDemo():
    def __init__(self, string):
        self.string = string
    def __repr__(self):
        return "String: {}".format(self.string)
    
if __name__ == "__main__":
    demo = FifthDemo("ChangeInfo")
    print(demo)
    
Out:
String: ChangeInfo

10.5.3 __del__()

__init__() 构造函数相反, __del__() 方法用于销毁类的实例对象。

大多数情况下,python会自动调用 __del__() 方法销毁不再使用的实例对象,并不需要手动释放,这里需要了解python的自动释放机制。

python使用自动引用计数(ARC)的方式实现垃圾回收,是一种类似于栈的机制。每个实例对象在初始化时计数器标记为0,当该对象被引用时计数器加1,反之取消对该对象的引用时计数器减1。当一个对象计数器归0时,代表该对象没有再被引用,python将自动将其销毁。

当重写某个父类不为【Object类】的子类中的 __del__() 方法时,需要显式调用父类的 __del__() 方法,否则无法保证资源被完全释放。

10.5.4 __dir__()

__dir__() 方法用于显示某个对象拥有的所有属性以及方法,返回类型为【列表】。

class FifthDemo():
    def __init__(self, string):
        self.string = string
        
if __name__ == "__main__":
    demo = FifthDemo("string")
    print(dir(demo))

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__', 'string']

可以看到,类中自然包含了__dir__() 方法,因此也可以写作 demo.__dir__() 同样会输出属性方法列表。

10.5.5 __dict__()

__dict__() 方法使用类名进行调用时会输出所有类属性组成的字典,而使用实例对象调用时会输出所有实例属性组成的字典。

class FifthDemo():
    string = "类属性"
    def __init__(self, string):
        self.string = string
        
if __name__ == "__main__":
    demo = FifthDemo("实例属性")
    print(FifthDemo.__dict__)
    print(demo.__dict__)
    
Out:
{'__module__': '__main__', 'string': '类属性', '__init__': <function FifthDemo.__init__ at 0x000001B16386D8B0>, '__dict__': <attribute '__dict__' of 'FifthDemo' objects>, '__weakref__': <attribute '__weakref__' of 'FifthDemo' objects>, '__doc__': None}
{'string': '实例属性'}

子类中调用 __dict__() 方法不会包含父类中的类属性,子类定义的实例对象调用 __dict__() 方法也不会包含父类的实例属性。

同时,可以使用【实例对象】调用 __dict__() 方法以字典的方式对其中实例属性进行修改。

10.5.6 hasattr()/getattr()/setattr()

hasattr() 用于判断某个【实例对象】中是否包含指定名称的属性或方法;

getattr() 用于获取某个【实例对象】中指定名称属性的值,可以通过指定未查找到时的返回值,如果不指定则会直接报错 AttributeError;

setattr() 用于修改某个【实例对象】的指定名称属性的值,也可以用来为实例对象动态添加属性或方法。

class FifthDemo():
    def __init__(self, string):
        self.string = string
        
if __name__ == "__main__":
    demo = FifthDemo("实例属性_1")
    print(hasattr(demo, 'string'))
    setattr(demo, 'word', '实例属性_2')
    print(getattr(demo, 'word', 'None'))
    
Out:
True
实例属性_2

10.5.7 issubclass()/isinstance()

issubclass(cls_1, cls_2/tuple) 用于检查【cls_1】是否为【cls_2】或【tuple】包含的多个类中任意类的子类;

isinstance(obj, cls/tuple) 用于检查【obj】是否为【cls】或【tuple】包含的多个类中任意类创建的实例对象。

10.5.8 __bases__ / __subclass__

__bases__ 用于查找某个类的所有直接父类,返回值为元组类型;

__subclass__ 用于查找某个类的所有直接子类,返回值为列表类型。

10.5.9 __call__()

__call__() 方法用于将类实例对象变为可调用对象。

可调用对象是指所有可以将 () 运用于自身并执行的对象,包括自定义函数、内置函数等。

同时,__call__() 方法还可以和 hasattr() 结合判断指定名称是属性还是方法

class FifthDemo():
    def __init__(self, string):
        self.string = string
    def __call__(self):
        print(self.string)
        
if __name__ == "__main__":
    demo = FifthDemo("实例属性")
    demo()
    demo.__call__()
    print(hasattr(demo.string, '__call__'))
    
Out:
实例属性
实例属性
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值