目录
例如使用__new__来创建一个实例,用来限制传入ATM的类中的id必须是以“id”开头的。
单例模式:(无论实例化多少次,实例化出来的多个对象实例都是同一个实例)
自定义元类,用于随机定义以后id,并且自定义id不能使用“_”开头
抽象基类:作用是定义接口规范(子类必须实现抽象基类里的抽象方法)
__init__和__new__的使用
在Python中,
__new__
是一个特殊的方法,用于在创建一个新对象(实例)时进行控制和定制。这个方法通常在类的定义中实现,并且是静态方法,意味着它不接收隐式的self
参数,而是接收一个表示类本身的cls
参数。当你创建一个新对象时,Python 会首先调用
__new__
方法来创建这个对象。然后,如果__new__
方法返回一个类的实例,接下来会调用__init__
方法来初始化这个实例,传递给__init__
的是self
参数。class MyClass: def __new__(cls, *args, **kwargs): # 在这里可以对新对象的创建进行控制和定制 # cls表示当前类本身,可以使用cls来创建新对象 # *args和**kwargs是传递给构造方法(__init__)的参数 new_instance = super().__new__(cls) # 对新对象进行必要的处理 return new_instance # 返回新对象 def __init__(self, *args, **kwargs): # 在这里进行实例的初始化操作 # self表示当前实例本身,可以使用self来访问实例的属性和方法 pass
在
__new__
方法中,你可以自定义对象的创建过程,例如控制创建实例的数量,返回已存在的实例(单例模式),或者在特定条件下返回其他类型的实例。
__new__
方法总结:
__new__
方法用于控制对象的创建过程,它返回一个类的实例。cls
参数表示当前类本身,可以使用它来创建新对象。*args
和**kwargs
是传递给构造方法__init__
的参数。__new__
和__init__
一起完成了对象的创建和初始化过程。
例如使用__new__来创建一个实例,用来限制传入ATM的类中的id必须是以“id”开头的。
# 先运行__new__,再运行__init__
# __new__需要返回一个当前类的实例(需要使用到retrun,返回参数给__init__)
# 一般不需要重写__new__, 如果对新创建的实例需要做一些限制的时候,可以重写new
class ATM(object):
# 后运行
# 实例方法:实例的实例化
def __init__(self, id):
print("This is init")
# 添加一个实例属性
self.id = id
# 先运行
# 创建实例(一般情况不需要重写的)
# 如果在创建实例的过程中,需要做一些检查/限制,可以重写new方法
# cls => 表示当前类,用于传递参数给__init__函数的self参数
def __new__(cls, *args, **kwargs): # object
print(cls, args, kwargs)
if not args or not args[0].startswith("id"): # startswith以什么开头 # not args表示传入的参数为空 # not args[0].startswith("id")表示传入参数的开头不为“id”的
raise ValueError("参数为空或id值没有以'id'开头") # 报错
# 返回当前类的实例的时候(return 运行的时候),__init__才会被自动调用
return object.__new__(cls) # 将cls参数传递给object类(ATM类)的__init__函数。
#测试
atm1 = ATM("id001")
print(atm1.id)
atm2 = ATM("001")
print(atm1.id)
#输出
<class '__main__.ATM'> ('id001',) {}
This is init
id001
<class '__main__.ATM'> ('001',) {}
Traceback (most recent call last):
File "D:\衡山-开发\python\pythonProject_day01\2023-07-24-python面向对象\10.元类.py", line 20, in <module>
atm2 = ATM("001")
File "D:\衡山-开发\python\pythonProject_day01\2023-07-24-python面向对象\10.元类.py", line 14, in __new__
raise ValueError("参数为空或id值没有以'id'开头")
ValueError: 参数为空或id值没有以'id'开头
单例模式:(无论实例化多少次,实例化出来的多个对象实例都是同一个实例)
在
__new__
方法中,你可以自定义对象的创建过程,例如控制创建实例的数量,返回已存在的实例(单例模式),或者在特定条件下返回其他类型的实例。定义一个类,无论实例化多少次,实例化出来的多个对象实例都是同一个实例
意思就是如果之前没有创建过这个实例,会帮助我们创建,如果创建过了,我们可以直接返回原来已经创建的实例。
class Singleton:
#类属性 :并且还是保护变量,该参数只能在该类中使用
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super().__new__(cls, *args, **kwargs) #是在一个类的 __new__ 方法中创建类的实例,并将该实例保存在类属性 _instance 中。这样的代码通常是实现单例模式的一种方式。
return cls._instance # 返回创建好的cls._instance类属性给__init__
#调用Singleton()类
s1 = Singleton()
s2 = Singleton()
print(s1 is s2)
#输出 True
super() 的使用
super() 是一个内置函数,在Python中用于调用父类的方法。它提供了一种方便的方式来访问父类的属性和方法,特别是在多重继承的情况下。
在Python中,当一个类继承自另一个类时,它可以使用 super() 函数来调用父类的方法。这样做的好处是,即使父类的名字发生改变,子类无需修改代码,仍然可以正确调用父类的方法。
super() 函数通常在子类的构造方法 __init__ 或其他方法中使用。通过 super().__init__(...) 可以调用父类的构造方法,从而完成父类的初始化操作。同样,可以使用 super().some_method(...) 来调用父类的其他方法。
#例子中,Child 类继承自 Parent 类。在 Child 类的构造方法中,通过 super().__init__(name) 调用了父类 Parent 的构造方法,从而初始化了父类的 name 属性。在 Child 类的 say_hello 方法中,使用 super().say_hello() 调用了父类的 say_hello 方法,并在此基础上添加了更多内容。
class Parent:
def __init__(self, name):
self.name = name
def say_hello(self):
print(f"Hello, I'm {self.name}")
class Child(Parent):
def __init__(self, name, age):
super().__init__(name) # 调用父类的构造方法,初始化父类的属性
self.age = age
def say_hello(self):
super().say_hello() # 调用父类的方法
print(f"I'm {self.age} years old.")
child = Child("Alice", 10)
child.say_hello()
使用类实现一个链表(单向、双向)
链表拓扑图:
单向链表:
单向链表(Singly Linked List)是一种常见的数据结构,它由一系列节点组成,每个节点包含两个部分:数据和指向下一个节点的指针(或引用)。链表中的节点按顺序连接在一起,形成一个线性序列。
每个节点包含两个主要字段:
数据部分:用于存储节点所代表的数据。
指针部分:用于指向链表中的下一个节点。在最后一个节点处,这个指针部分通常指向空值(null),表示链表的结束。
示例单向链表结构: Node Node Node None +--------+------+ +--------+------+ +--------+------+ +------+ | data | next | -> | data | next | -> | data | next | -> | None | +--------+------+ +--------+------+ +--------+------+ +------+ | 1 | ------->| 2 | ------->| 3 | ------->| None | +--------+------+ +--------+------+ +--------+------+ +------+
在上述示例中,每个节点由数据部分和指针部分组成。第一个节点(头节点)包含数据为 1,指针指向下一个节点;第二个节点包含数据为 2,指针指向下一个节点;第三个节点包含数据为 3,指针指向空值(表示链表结束)。
# 实现链表的类型Node类
class Node(object):
def __init__(self, value, next=None):
self.value = value
# next 为Node的一个实例
self.next = next
# 产生一个单链
link = None
# prev_node 创建用于记录上一个节点
prev_node = None
for i in range(10):
# 创建一个实例(表示创建节点)
cur_node = Node(i)
# 如果link为None,当前节点作为头节点
# 如果link不为None,则表示为头节点后面的节点
if link is None:
link = cur_node
else:
# prev_node.next 表示将上一个节点的next值保存为现在这个节点的地址
prev_node.next = cur_node
#将现在这个节点的信息保存到前一个节点上去,节点往后移
prev_node = cur_node
# 输出单链的信息
print(link.value)
while link.next:
print(link.next.value)
link = link.next
输出为:
0
1
2
3
4
5
6
7
8
9
双向链表:
双向链表(Doubly Linked List)是一种链表的变种,每个节点除了有指向下一个节点的指针外,还有指向上一个节点的指针。这使得双向链表中的节点可以在双向上进行遍历,而不仅限于单向遍历。
每个节点包含三个主要字段:
数据部分:用于存储节点所代表的数据。
指向下一个节点的指针(或引用):用于指向链表中的下一个节点。
指向上一个节点的指针(或引用):用于指向链表中的上一个节点。
示例双向链表结构: None <- Node Node Node -> None +------+--------+ +------+--------+ +------+--------+ | prev | data | | prev | data | | prev | data | None <- | | 1 | <- | | 2 | <- | | 3 | -> None | | | | | | | | | +------+--------+ +------+--------+ +------+--------+
在上述示例中,每个节点有两个指针,一个指向上一个节点(
prev
),一个指向下一个节点(next
)。第一个节点(头节点)指向上一个节点为None
,数据为 1,指向下一个节点;第二个节点指向上一个节点为第一个节点,数据为 2,指向下一个节点;第三个节点指向上一个节点为第二个节点,数据为 3,指向下一个节点;最后一个节点(尾节点)指向下一个节点为None
。
class Node(object):
def __init__(self, value, next=None, prev=None):
self.value = value
# next => Node的一个实例
self.next = next
self.prev = prev
# 产生一个单链
link = None
prev_node = None
for i in range(10):
# 创建一个实例
cur_node = Node(i)
# 如果link为None,当前节点作为头节点
if link is None:
link = cur_node
else:
prev_node.next = cur_node
# cur_node.prev 表示这一个节点信息的前一个个节点,就是前面一个节点节点
cur_node.prev = prev_node
prev_node = cur_node
#从前到后输出链表
print(link.value)
while link.next:
print(link.next.value)
link = link.next
#从后到前输出列表
print(link.value)
while link.prev:
print(link.prev.value)
link = link.prev
输出为:
0
1
2
3
4
5
6
7
8
9
9
8
7
6
5
4
3
2
1
0
类的方法:
方法
# __init__: 实例初始化
# __new__:创建实例# __doc__: 查看文章注释
# __module: 查看当前我们的对象在哪个模块里面
# __dict__: 通过
__dict__
字典来获取对象的所有属性和对应的值,通过字典的方式输出
# __repr__: 用友好的输出显示实例的信息(正式地打印)
# __str__: 用友好的输出显示实例的信息
# __eq__: 相等
# __ge__:大于等于
# __gt__: 大于
# __le__: 小于等于
# __lt__: 小于
# __ne__:不等于
# __setattr__ : a.value=100 设置属性的时候调用
# __getattr__ : a.value 获取属性的时候被调用
# __del__: 当实例没删除的时候被调用
# __call__:__call__
方法时,该类的实例可以像函数一样被调用。
# TypeError: 'A' object is not callable(没有存在__call__方法的时候报的错)
# callable => 可调用的 => 定义了__call__方法
class A(object):
"""注释:xxx"""
# 初始化实例
def __init__(self, value):
self.value = value
def show(self):
pass
def __str__(self):
# 用友好的输出显示实例的信息
return f"class A instance:{self.value}"
def __repr__(self):
# 用友好的输出显示实例的信息
return f"repr: class A instance:{self.value}"
def __gt__(self, other):
# 重写大于方法 a1 > a2 => a1.__gt__(a2)
if self.value > other.value:
return True
return False
def __ge__(self, other):
if self.value >= other.value:
return True
return False
# __call__ 方法时,该类的实例可以像函数一样被调用
def __call__(self, *args, **kwargs):
print(f"{self.value}被调用了",args, kwargs)
# 当实例没删除的时候, 删除实例
def __del__(self):
print(f"{self.value}被删除了")
a1 = A(100)
print(dir(a1))
print(a1.__dict__)
# ['__delattr__', '__format__', '__getattribute__', '__hash__', '__init_subclass__', '', '__reduce__', '__reduce_ex__', '', '', '__sizeof__', '__subclasshook__', '__weakref__',]
# 属性
print(a1.__class__)
print(a1.__doc__)
print(a1.__module__)
print(a1.__dict__)
print(a1)
li = [a1]
print(li)
a2 = A(200)
# a1.__lt__(a2)
print(a1 > a2)
print(a1 < a2)
print(a1 >= a2) # a1.__ge__(a2)
print(a1 != a2)
# print(a1.__dir__)
# 方法
# __init__: 实例初始化
# __new__:创建实例
# __repr__: 用友好的输出显示实例的信息(正式地打印)
# __str__: 用友好的输出显示实例的信息
# __eq__: 相等
# __ge__: 大于等于
# __gt__: 大于
# __le__:小于等于
# __lt__:小于
# __ne__:不等于
# __setattr__ : a.value=100 设置属性的时候调用
# __getattr__ : a.value 获取属性的时候被调用
# __del__: 当实例没删除的时候
# __call__: __call__ 方法时,该类的实例可以像函数一样被调用
# TypeError: 'A' object is not callable
# callable => 可调用的 => 定义了__call__方法
# 当实例被像函数一样被调用的时候
# 将对象当作函数调用,实际上是调用 __call__ 方法
a1()
a1(1,a=1)
元类:
元类(Metaclass)是Python中一个高级而强大的概念,它用于控制类的创建和实例化过程。在Python中,一切都是对象,类也是对象的一个实例,而元类就是用于创建这些类的类。
在介绍元类之前,我们需要了解以下几个概念:
类(Class):是用于创建对象的蓝图,它定义了对象的属性和方法。
实例化(Instantiation):是通过类创建对象的过程,创建的对象称为类的实例。
类对象(Class Object):在Python中,类也是对象的一种,它是元类创建的实例。
元类的主要作用是在类对象创建过程中拦截、修改和定制类的行为。元类可以控制类的属性、方法、继承关系等。Python中,我们可以通过定义元类并在类的声明中指定使用该元类来实现类的定制。
下面是一个简单的元类示例:
# 定义一个元类
class MyMeta(type):
def __new__(cls, name, bases, attrs):
# 在类创建前进行定制,可以修改属性、方法等
attrs['x'] = 10
return super().__new__(cls, name, bases, attrs)
# 使用元类创建类
class MyClass(metaclass=MyMeta):
def print_x(self):
print(self.x)
# 创建类的实例并调用方法
obj = MyClass()
obj.print_x() # 输出: 10
在上面的示例中,我们定义了一个元类
MyMeta
,它继承自type
,这是所有Python类的默认元类。在元类的__new__
方法中,我们对类的属性进行了定制,给类添加了一个新的属性x
并赋值为 10。然后,我们使用这个元类来创建类MyClass
,在类的声明中指定了metaclass=MyMeta
。当我们创建MyClass
类的实例时,元类的__new__
方法会被调用,在创建类对象之前对类进行了定制。
type
是所有Python类的默认元类。
# 元类是用来创建类的类,type是一个元类(最底层的元类)
#元类是创建类的类 -- 最上层的元类就是type
class A(object):
pass
# 底层使用的是type
a1 = A()
print(type(a1))
print(type(A))
print(type(type))
# 使用type创建B类
B = type('B',(object,),{})
b1 = B()
print(A)
print(B)
print(b1)
自定义元类,用于随机定义以后id,并且自定义id不能使用“_”开头
import random
# 自定义元类:一个类继承自type,它就是一个自定义元类
# 自定义元类 -- 拦截类的创建
class ModelMeta(type): #元类
def __init__(self, *args, **kwargs):
print('init', args, kwargs)
self.id = random.randint(1,100)
def __new__(cls, *args, **kwargs):
print('new', args, kwargs)
# 获取所有的属性和方法,检查自定义属性名中有没有以“_”开头的。
for item in args[2].keys():
if item.startswith("_") and item not in ['__module__','__qualname__']:
raise NameError(f"标识符不能以划线开头:{item}")
return super().__new__(cls, *args, **kwargs)
# 添加一个id属性,id值,随机1-100
class ModelA(object, metaclass=ModelMeta): #使用ModelMeta元类方法创建的ModelA
a = 1
b = 2
# 继承ModelA
class ModelB(ModelA):
c = 3
# 执行代码
m1 = ModelA()
m2 = ModelB()
print(m1.id, m2.id)
#输出
new ('ModelA', (<class 'object'>,), {'__module__': '__main__', '__qualname__': 'ModelA', 'a': 1, 'b': 2}) {}
init ('ModelA', (<class 'object'>,), {'__module__': '__main__', '__qualname__': 'ModelA', 'a': 1, 'b': 2}) {}
new ('ModelB', (<class '__main__.ModelA'>,), {'__module__': '__main__', '__qualname__': 'ModelB', 'c': 3}) {}
init ('ModelB', (<class '__main__.ModelA'>,), {'__module__': '__main__', '__qualname__': 'ModelB', 'c': 3}) {}
30 8
元类的使用(ABC类 --》 抽象基类 --> 模拟接口)
抽象基类:作用是定义接口规范(子类必须实现抽象基类里的抽象方法)
python 中没有接口类型 --》接口类型 => 对象中声明了一些方法,但是没有实现
=> 子类中才具体去实现这些方法(如果没有实现,会报错)
在 Python 中,没有像其他编程语言(如 Java)中那样的显式接口定义。Python 不支持类似于 Java 接口(Interface)的概念,即接口中定义方法的签名而不提供具体实现,然后类可以实现该接口并提供相应的方法实现。
然而,在 Python 中,可以使用抽象基类(Abstract Base Classes,ABC)来模拟接口的行为。抽象基类是一种特殊的类,它不能直接实例化,而是用于定义接口或者部分接口,其中可以包含抽象方法(没有具体实现的方法)。其他类可以继承抽象基类并实现其中的抽象方法,从而达到类似接口的效果。
Python 提供了
abc
模块来支持抽象基类的定义和使用。我们可以通过继承abc.ABC
类,并使用@abstractmethod
装饰器来定义抽象方法。子类必须实现抽象方法,否则会抛出TypeError
。以下是一个使用抽象基类定义接口的示例:
from abc import ABC, abstractmethod class Shape(ABC): @abstractmethod def area(self): pass @abstractmethod def perimeter(self): pass class Circle(Shape): def __init__(self, radius): self.radius = radius def area(self): return 3.14 * self.radius * self.radius def perimeter(self): return 2 * 3.14 * self.radius class Rectangle(Shape): def __init__(self, width, height): self.width = width self.height = height def area(self): return self.width * self.height def perimeter(self): return 2 * (self.width + self.height) # 无法实例化抽象基类 # shape = Shape() # 报错:TypeError: Can't instantiate abstract class Shape with abstract methods area, perimeter circle = Circle(5) rectangle = Rectangle(4, 6) print(circle.area()) # 输出:78.5 print(circle.perimeter()) # 输出:31.400000000000002 print(rectangle.area()) # 输出:24 print(rectangle.perimeter()) # 输出:20
在上述示例中,我们定义了一个抽象基类
Shape
,其中包含两个抽象方法area
和perimeter
。然后,我们定义了两个子类Circle
和Rectangle
,分别继承了Shape
抽象基类并实现了抽象方法。
ABC 类使用的例子:
from abc import ABC, abstractmethod
class Test(ABC):
@abstractmethod
def test1(self):
pass
@abstractmethod
def test2(self):
pass
class TestA(Test):
def test1(self):
print("this is TestA, test1")
def test2(self):
print("this is TestA, test2")
# 如果子类没有实现抽象方法,实例化的时候会报错
t1 = TestA()