面试必问(1):python类的__init__和__new__的区别和使用场景

今天来整理下面试中常问到的类中__init____new__的区别和使用场景。
在 Python 中,__init____new__ 是两个在类的实例化过程中发挥重要作用的方法,但它们各自的作用和使用场景是不同的。

概念区别

__new__

__new__ 是一个静态方法,用于创建实例。它是类的实例化过程中被调用的第一个方法。__new__ 接受类本身(cls)和其他参数,并返回一个实例。一般情况下,除非你有特殊需求,你可能很少需要覆盖 __new__ 方法,写一些底层框架代码的时候可能会用上。

__init__

__init__ 是实例初始化方法。它在 __new__ 创建实例后被调用,用于初始化实例的属性和设置状态。
__init__ 返回 None,它不负责返回实例,也就是说__init__中的self参数其实是__new__方法的返回值。
以下是一个简单示例,展示了__new____init__ 的使用与区别:

class MyClass:
    def __new__(cls, *args, **kwargs):
        print("Calling __new__")
        instance = super(MyClass, cls).__new__(cls)
        # 在这里,你可以控制实例的创建,比如使用单例模式或者工厂模式
        return instance

    def __init__(self, value):
        print("Calling __init__")
        self.value = value

# 创建实例
my_instance = MyClass(42)

# Outputs
# Calling __new__
# Calling __init__

__new__ 方法首先被调用,负责创建实例,然后 __init__ 方法负责初始化实例。

常用使用场景

__new__的使用

new 方法在 Python 中可以用于控制类的实例创建过程,包括实现单例模式工厂模式缓存实例以及预防不合适的实例化等。以下是每个使用场景的代码示例:

单例模式

在单例模式中,__new__ 方法确保类只会有一个实例,并返回同一个实例给每次实例化调用,这个是__new__最常用的场景。

class Singleton:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super(Singleton, cls).__new__(cls)
        return cls._instance

# 使用示例
s1 = Singleton()
s2 = Singleton()


print(s1 == s2)  # 输出 True,说明两者是同一个实例
工厂模式

在工厂模式中,new 方法可以根据传入的参数返回不同类型的实例。

class AuthStrategy:
    def authenticate(self):
        raise NotImplementedError


class WechatStrategy(AuthStrategy):
    def authenticate(self):
        return "Wechat authenticate"


class EmailStrategy(AuthStrategy):
    def authenticate(self):
        return "Email authenticate"


class AuthlFactory:
    def __new__(cls, auth_type):
        if auth_type == 'wechat':
            return WechatStrategy()
        elif auth_type == 'email':
            return EmailStrategy()
        else:
            raise ValueError("Unknown animal type")


# 使用示例
w = AuthlFactory('wechat')
e = AuthlFactory('email')

print(w.authenticate())  # 输出 "Wechat authenticate"
print(e.authenticate())  # 输出 "Email authenticate"

缓存实例

在缓存实例中,__new__ 方法可以缓存创建的实例,并在重复创建时返回缓存的实例。

class CacheObject:
    _cache = {}

    def __new__(cls, key):
        if key in cls._cache:
            return cls._cache[key]
        else:
            instance = super(CacheObject, cls).__new__(cls)
            cls._cache[key] = instance
            return instance

# 使用示例
obj1 = CacheObject('key1')
obj2 = CacheObject('key1')

print(obj1 == obj2)  # 输出 True,说明两者是同一个实例

预防不合适的实例化

在预防不合适的实例化场景中,new 方法可以通过抛出异常来防止实例化不合适的对象。

class RestrictedInstantiation:
    def __new__(cls, flag):
        if not flag:
            raise ValueError("flag必须为True")
        return super(RestrictedInstantiation, cls).__new__(cls)

# 使用示例
try:
    restricted_instance = RestrictedInstantiation(False)
except ValueError as e:
    print(e)  # 输出 "flag必须为True"

a = RestrictedInstantiation(True)
print("成功创建")  # 这将被打印,因为 flag 是 True

以上示例展示了 __new__ 方法在不同使用场景中的应用,包括单例模式、工厂模式、缓存实例和预防不合适的实例化。

__init__的使用

__init__ 方法在 Python 中用于初始化类的实例。这是类实例化后的第一个方法,它主要用于设置实例的属性和初始化实例的状态,将接下来可能需要的数据或者资源(文件)都准备好。以下是 __init__ 方法的不同使用场景及代码示例:

初始化实例属性

__init__ 方法通常用于初始化实例的属性,以便实例在创建时具备必要的状态。

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

# 使用示例
person = Person("Mike", 30)
print(person.name)  # 输出 "Mike"
print(person.age)   # 输出 30

参数验证和处理

__init__ 方法可以用于验证和处理传入的参数,以确保实例属性符合预期。

import re


class Account:
    # 定义正则表达式来验证电子邮件格式
    EMAIL_PTN = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'

    def __init__(self, email):
        if not self.is_valid_email(email):
            raise ValueError("不正确email格式")
        self.email = email

    @staticmethod
    def is_valid_email(email: str) -> bool:
        # 使用正则表达式匹配电子邮件地址
        return re.match(Account.EMAIL_PTN, email) is not None

# 使用示例
try:
    account = Account("111")
except ValueError as e:
    print(e)  # 输出 "不正确email格式"

valid_account = Account("xmishu@xmishu.com")
print(valid_account.email)   # 输出 "xmishu@xmishu.com"
计算派生属性

__init__ 方法可以用于根据输入参数计算派生附加属性,从而在实例创建时提供额外的信息。

class Rectangle:
    def __init__(self, length, width):
        self.length = length
        self.width = width
        self.area = length * width  # 计算面积
        self.perimeter = 2 * (length + width)  # 计算周长

# 使用示例
rect = Rectangle(4, 30)
print(rect.area)      # 输出 120
print(rect.perimeter) # 输出 68

通过这些例子,你可以看到 __init__ 方法在初始化实例属性、验证和处理参数、计算派生属性等场景中的应用。

总结

__new__ 是一个静态方法,用于创建实例(不一定是新的)并返回这个实例;__init__ 是实例初始化方法,设置属性初始状态,它返回的是None。

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值