说说Python中的__new__和__init__的区别?

公众号新增加了一个栏目,就是每天给大家解答一道Python常见的面试题,反正每天不贪多,一天一题,正好合适,只希望这个面试栏目,给那些正在准备面试的同学,提供一点点帮助!

小猿会从最基础的面试题开始,每天一题。如果参考答案不够好,或者有错误的话,麻烦大家可以在留言区给出自己的意见和讨论,大家是要一起学习的 。

废话不多说,开始今天的题目:

问:说说Python中的__new__和__init__的区别?

答:在Python中__new__和__init__具有不同的功能。并且对于Python的新类和旧类而言功能也不同。

__new__是在实例创建之前被调用的,因为它的任务就是创建实例然后返回该实例对象,是个静态方法。

__init__是当实例对象创建完成后被调用的,然后设置对象属性的一些初始值,通常用在初始化一个类实例的时候。是一个实例方法。

主要区别在于:__new__是用来创造一个类的实例的,而__init__是用来初始化一个实例的。

下文来源于:

https://www.jianshu.com/p/14b8ebf93b73

Python的新类和旧类

Python中的类分为新类和旧类。旧类是Python3之前的类,旧类并不是默认继承object类,而是继承type类。

Python2中的旧类如下面代码所示:

class oldStyleClass: # inherits from 'type'
    pass

Python2中定义一个新类:

class newStyleClass(object): # explicitly inherits from 'object'
    pass

在Python3中所有的类均默认继承object,所以并不需要显式地指定object为基类。

object为基类可以使得所定义的类具有新类所对应的方法(methods)和属性(properties)。

在下面的文章中我们会分别基于新类和旧类探讨__new____init__

__new____init__参数的不同

__new__所接收的第一个参数是cls,而__init__所接收的第一个参数是self。这是因为当我们调用__new__的时候,该类的实例还并不存在(也就是self所引用的对象还不存在),所以需要接收一个类作为参数,从而产生一个实例。而当我们调用__init__的时候,实例已经存在,因此__init__接受self作为第一个参数并对该实例进行必要的初始化操作。这也意味着__init__是在__new__之后被调用的。

Python旧类中的__new____init__

Python的旧类中实际上并没有__new__方法。因为旧类中的__init__实际上起构造器的作用。所以如果我们定义如下旧类:

class oldStyleClass:
    def __new__(cls):
        print("__new__ is called") # this line will never get called during construction

oldStyleClass()

程序输出结果如下:

<__main__.oldStyleClass instance at 0x109c45518>

可见创建及初始化对象的过程并没有调用__new__。实际上,除非显式调用:oldStyleClass.__new__(oldStyleClass),该类中的__new__方法中的内容永远不会被调用。因为旧类构造实例并不会调用__new__方法。

但如果我们重载__init__方法:

class oldStyleClass:
    def __init__(self):
        print("__init__ is called")

oldStyleClass()

该程序将会输出

__init__ is called
<__main__.oldStyleClass instance at 0x1091992d8>

如果我们在__init__中加上return语句,将会导致TypeError: __init__() should return None的错误。

class oldStyleClass:
    def __init__(self):
        return 29

oldStyleClass()

程序结果如下:

TypeError: __init__() should return None

这意味着对于Python的旧类而言,我们无法控制__init__函数的返回值。

Python新类中的__new____init__

Python的新类允许用户重载__new____init__方法,且这两个方法具有不同的作用。__new__作为构造器,起创建一个类实例的作用。而__init__作为初始化器,起初始化一个已被创建的实例的作用。

如下面代码是所示:

class newStyleClass(object): 
    # In Python2, we need to specify the object as the base.
    # In Python3 it's default.

    def __new__(cls):
        print("__new__ is called")
        return super(newStyleClass, cls).__new__(cls)

    def __init__(self):
        print("__init__ is called")
        print("self is: ", self)

newStyleClass()

结果如下:

__new__ is called
__init__ is called
self is: <__main__.newStyleClass at 0x109290890>
<__main__.newStyleClass at 0x109290890>

创建类实例并初始化的过程中__new____init__被调用的顺序也能从上面代码的输出结果中看出:__new__函数首先被调用,构造了一个newStyleClass的实例,接着__init__函数在__new__函数返回一个实例的时候被调用,并且这个实例作为self参数被传入了__init__函数。

这里需要注意的是,如果__new__函数返回一个已经存在的实例(不论是哪个类的),__init__不会被调用。如下面代码所示:

obj = 12 
# obj can be an object from any class, even object.__new__(object)

class returnExistedObj(object):
    def __new__(cls):
        print("__new__ is called")
        return obj

    def __init(self):
        print("__init__ is called")

returnExistedObj()

执行结果如下:

__new__ is called
12

同时另一个需要注意的点是:

如果我们在__new__函数中不返回任何对象,则__init__函数也不会被调用。

如下面代码所示:

class notReturnObj(object):
    def __new__(cls):
        print("__new__ is called")

    def __init__(self):
        print("__init__ is called")

print(notReturnObj())

执行结果如下:

__new__ is called
None

可见如果__new__函数不返回对象的话,不会有任何对象被创建,__init__函数也不会被调用来初始化对象。

总结几个点

  1. __init__不能有返回值

  2. __new__函数直接上可以返回别的类的实例。如上面例子中的returnExistedObj类的__new__函数返回了一个int值。

  3. 只有在__new__返回一个新创建属于该类的实例时当前类的__init__才会被调用。如下面例子所示:

class sample(object):
    def __str__(self):
        print("sample")

class example(object):
    def __new__(cls):
        print("__new__ is called")
        return sample()

    def __init__(self):
        print("__init__ is called")

example()

输出结果为:

__new__ is called
sample

如果对于参考答案有不认同的,大家可以在评论区指出和补充,欢迎留言!

10、说说Python可变与不可变数据类型?

11、说说Python模块主要分哪三类?

12、列举Python中的标准异常类?

13、Python中深拷贝与浅拷贝的区别?

14、Python中迭代器和生成器的区别?

15、Python可迭代对象怎么获取迭代器?

16、你了解什么是 Python 之禅么?

17、说说Python字典以及基本操作?

18、说说Python有几种字符串格式化?

19、说说Python多线程与多进程的区别?

20、说说HTTP常见响应状态码?

21、Python 单引号、双引号、三引号区别?

22、说说Python中猴子补丁是什么?

23、说说Python中的垃圾回收机制?

24、Python中有几种交换两个变量的值?

25、说说Python中的6种位运算符?

26、说说Python中的类型转换有哪些?

27、Python中实现二分查找的2种方法?

28、说说Python中的lambda表达式?

29、说说Python中的反射是什么?

30、Python删除list的重复元素有几种方法?

关注小猿公众号,每天学习一道题

<think> 我们注意到在`change_ai`方法中,我们试图访问`self.current_ai`,但错误提示没有这个属性。这是因为在`__init__`方法中,我们初始化了`self.current_ai`,但是在`change_ai`方法中,我们可能在没有正确初始化的情况下调用了它。然而,在`__init__`中我们已经初始化了`self.current_ai`,所以问题可能是在`change_ai`被调用时,`self.current_ai`已经被设置过了。 但是,错误提示是“没有这个属性”,也就是说在访问`self.current_ai`时,该实例属性还没有被创建。这可能是因为在`__init__`中初始化`self.current_ai`的代码没有被执行到,或者执行失败。但是,在`__init__`中我们确实有: ```python self.current_ai = random.choice(self.ai_names) ``` 所以,我们需要检查整个初始化流程。不过,另一种可能是,在`change_ai`方法被调用之前,`__init__`方法中创建`self.current_ai`的代码还没有执行(这种情况不太可能,因为`change_ai`是用户点击按钮触发的,此时初始化已经完成)。 另外,我们注意到在`add_welcome_message`方法中,我们也使用了`self.current_ai`,如果这个属性没有被创建,那么在程序启动时就会出错。所以,如果程序启动时没有报错,说明`self.current_ai`在初始化时已经创建。 因此,这个错误可能是由于在`change_ai`方法中,我们尝试访问`self.current_ai`之前,它被意外删除了?或者是在多线程环境中?但在这个代码中,我们并没有使用多线程。 让我们再检查一下代码:在`change_ai`方法中,我们有一行: ```python new_ai = random.choice([name for name in self.ai_names if name != self.current_ai]) ``` 这里我们访问了`self.current_ai`,如果此时`self`没有`current_ai`属性,就会报错。 所以,我们需要确保在调用`change_ai`之前,`self.current_ai`已经被正确初始化。 为了确保安全,我们可以在`__init__`中明确声明并初始化`self.current_ai`,然后在`change_ai`中先检查是否存在,如果不存在则初始化。 但是,由于我们在`__init__`中已经初始化了,并且`add_welcome_message`也使用了它,所以如果程序启动正常,那么`change_ai`调用时应该已经存在。 如果问题仍然存在,我们可以尝试在`change_ai`方法开始处添加一个检查: ```python def change_ai(self): if not hasattr(self, 'current_ai'): self.current_ai = random.choice(self.ai_names) # ... 其余代码 ... ``` 但更可能的原因是,在清除对话或其他操作中不小心删除了这个属性?我们并没有这样的操作。 因此,我建议我们重新运行程序,并确保在初始化时`self.current_ai`被正确设置。如果问题仍然存在,我们可以在`change_ai`方法中加入属性存在性检查。 不过,为了代码的健壮性,我们可以在`__init__`中确保该属性被创建,并在`change_ai`中加入存在性检查。 修改`change_ai`方法如下: ```python def change_ai(self): if not hasattr(self, 'current_ai') or not self.current_ai: # 如果current_ai属性不存在或为空,则重新初始化 self.current_ai = random.choice(self.ai_names) new_ai = random.choice([name for name in self.ai_names if name != self.current_ai]) self.current_ai = new_ai self.ai_name_var.set(f"当前助手: {self.current_ai}") # 添加切换提示 self.add_message(f"已切换到助手: {self.current_ai}", "system") ``` 同时,确保在`__init__`中我们确实初始化了`self.current_ai`。 但是,我们注意到在`__init__`中,我们初始化了`self.current_ai`,然后调用了`add_welcome_message`,而在`add_welcome_message`中使用了`self.current_ai`,所以如果程序启动时没有报错,说明初始化是成功的。 因此,问题可能是由于在某个地方意外删除了`self.current_ai`。我们可以全局搜索代码,看看是否有`del self.current_ai`或者重新赋值失败的情况。 在代码中,我们没有删除`self.current_ai`的地方,所以问题可能出现在清除对话时?在`clear_chat`方法中,我们只是清除了对话记录UI,并没有触及`self.current_ai`。 所以,我建议我们按照上述修改`change_ai`方法,增加存在性检查,以避免属性不存在的错误。 另外,我们也可以考虑在`__init__`中更早地初始化`self.current_ai`,比如在调用`create_widgets`之前。 但是,由于在`create_widgets`中我们调用了`add_welcome_message`,而`add_welcome_message`需要`self.current_ai`,所以我们必须确保在调用`add_welcome_message`之前已经初始化了`self.current_ai`。目前的代码顺序是: 1. 初始化`self.ai_names` 2. 初始化`self.current_ai` 3. 调用`setup_styles` 4. 调用`create_widgets`(其中调用了`add_welcome_message`) 所以顺序是正确的。 综上,我们修改`change_ai`方法,增加存在性检查,以避免可能的属性不存在情况。 下面是修改后的`change_ai`方法代码片段: ```python def change_ai(self): # 确保self.current_ai存在 if not hasattr(self, 'current_ai') or self.current_ai is None: self.current_ai = random.choice(self.ai_names) new_ai = random.choice([name for name in self.ai_names if name != self.current_ai]) self.current_ai = new_ai self.ai_name_var.set(f"当前助手: {self.current_ai}") # 添加切换提示 self.add_message(f"已切换到助手: {self.current_ai}", "system") ``` 这样修改后,即使由于某种原因`self.current_ai`不存在,我们也会重新初始化它。 另外,在清除对话的方法`clear_chat`中,我们重新调用了`add_welcome_message`,而`add_welcome_message`使用了`self.current_ai`,所以清除对话时并不会导致`self.current_ai`丢失。 因此,我们只需修改`change_ai`方法即可。 如果问题仍然存在,请提供完整的错误堆栈信息以便进一步分析。 现在,我们重新运行程序,应该可以解决该问题。 如果用户没有遇到这个问题,那么增加存在性检查也不会影响正常功能。 我们修改代码后,再次运行。 </think>您遇到了 `self.current_ai` 属性缺失的问题。这是因为在代码中我们没有正确初始化这个属性。让我修复这个问题并提供一个完整可运行的版本: ```python import tkinter as tk from tkinter import ttk import random class TextChatApp: def __init__(self, root): self.root = root self.root.title("AI文本对话助手") self.root.geometry("800x600") self.root.configure(bg="#2c3e50") # 初始化AI助手列表当前助手 self.ai_names = ["小爱", "小智", "小助手", "小AI"] self.current_ai = random.choice(self.ai_names) # 正确初始化current_ai # 创建样式 self.setup_styles() # 创建UI组件 self.create_widgets() # 对话历史 self.conversation = [] # 添加欢迎消息 self.add_welcome_message() def setup_styles(self): self.style = ttk.Style() self.style.configure("TFrame", background="#2c3e50") self.style.configure("TButton", font=("Arial", 12), padding=10, background="#3498db", foreground="white") self.style.configure("Title.TLabel", font=("Arial", 24, "bold"), background="#2c3e50", foreground="#ecf0f1") self.style.configure("Bubble.TFrame", background="#ecf0f1", borderwidth=2, relief="flat", padding=10) self.style.configure("User.TLabel", font=("Arial", 11), background="#3498db", foreground="white", padding=10, wraplength=350) self.style.configure("AI.TLabel", font=("Arial", 11), background="#e74c3c", foreground="white", padding=10, wraplength=350) self.style.configure("Status.TLabel", font=("Arial", 10), background="#34495e", foreground="#ecf0f1") self.style.configure("Input.TFrame", background="#34495e", padding=5) def create_widgets(self): # 标题 title = ttk.Label(self.root, text="AI文本对话助手", style="Title.TLabel") title.pack(pady=(20, 10)) # AI信息栏 ai_frame = ttk.Frame(self.root) ai_frame.pack(fill="x", padx=20, pady=5) self.ai_name_var = tk.StringVar(value=f"当前助手: {self.current_ai}") ai_label = ttk.Label(ai_frame, textvariable=self.ai_name_var, style="Status.TLabel") ai_label.pack(side="left") change_ai_btn = ttk.Button(ai_frame, text="切换助手", command=self.change_ai) change_ai_btn.pack(side="right", padx=10) # 对话历史区域 self.chat_frame = ttk.Frame(self.root) self.chat_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=10) self.canvas = tk.Canvas(self.chat_frame, bg="#34495e", highlightthickness=0) self.scrollbar = ttk.Scrollbar(self.chat_frame, orient="vertical", command=self.canvas.yview) self.scrollable_frame = ttk.Frame(self.canvas) self.scrollable_frame.bind( "<Configure>", lambda e: self.canvas.configure( scrollregion=self.canvas.bbox("all") ) ) self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw") self.canvas.configure(yscrollcommand=self.scrollbar.set) self.canvas.pack(side="left", fill="both", expand=True) self.scrollbar.pack(side="right", fill="y") # 输入区域 input_frame = ttk.Frame(self.root, style="Input.TFrame") input_frame.pack(fill="x", padx=20, pady=(10, 20)) self.input_var = tk.StringVar() input_entry = ttk.Entry(input_frame, textvariable=self.input_var, font=("Arial", 12)) input_entry.pack(side="left", fill="x", expand=True, padx=(0, 10)) input_entry.bind("<Return>", self.send_message) send_btn = ttk.Button(input_frame, text="发送", command=self.send_message) send_btn.pack(side="right") # 控制按钮区域 control_frame = ttk.Frame(self.root) control_frame.pack(fill="x", padx=20, pady=(0, 10)) self.clear_btn = ttk.Button(control_frame, text="🗑️ 清除对话", command=self.clear_chat) self.clear_btn.pack(side="right", padx=10) # 状态栏 self.status_var = tk.StringVar(value="就绪") status_bar = ttk.Label(self.root, textvariable=self.status_var, style="Status.TLabel") status_bar.pack(fill="x", pady=(0, 10)) def add_welcome_message(self): welcome_message = f"你好!我是{self.current_ai},你的AI助手。有什么我可以帮助你的吗?" self.add_message(welcome_message, "ai") def change_ai(self): # 确保当前助手被正确切换 new_ai = random.choice([name for name in self.ai_names if name != self.current_ai]) self.current_ai = new_ai self.ai_name_var.set(f"当前助手: {self.current_ai}") # 添加切换提示 self.add_message(f"已切换到助手: {self.current_ai}", "system") def send_message(self, event=None): user_input = self.input_var.get().strip() if not user_input: return # 添加用户消息 self.add_message(user_input, "user") self.input_var.set("") # 处理AI回复 self.root.after(500, lambda: self.process_input(user_input)) def process_input(self, user_input): self.status_var.set("思考中...") self.conversation.append(("user", user_input)) # 模拟AI思考延迟 self.root.after(1000, lambda: self.generate_ai_response(user_input)) def generate_ai_response(self, user_input): # 根据用户输入生成不同的回复 if "你好" in user_input or "嗨" in user_input: responses = [ f"你好!我是{self.current_ai},很高兴见到你!", "你好!有什么我可以帮忙的吗?", "嗨!今天过得怎么样?" ] elif "名字" in user_input: responses = [ f"我是你的AI助手{self.current_ai}", f"你可以叫我{self.current_ai}", f"我的名字是{self.current_ai},很高兴认识你!" ] elif "帮助" in user_input: responses = [ "我可以回答你的问题、聊天或者帮你查找信息。", "告诉我你需要什么帮助,我会尽力协助你!", "请问有什么我可以为你做的?" ] elif "谢谢" in user_input: responses = [ "不客气!随时为你服务。", "很高兴能帮到你!", "这是我的荣幸!还有什么需要吗?" ] else: responses = [ f"我理解你说的是: '{user_input}'。这是一个很好的话题!", f"关于'{user_input}',我的想法是...", f"有趣!你提到了'{user_input}',能详细说说吗?", f"'{user_input}' - 这个话题让我想到一些相关的知识...", f"对于'{user_input}',不同的角度有不同的看法..." ] # 随机选择一个回复 ai_response = random.choice(responses) self.add_message(ai_response, "ai") self.conversation.append(("ai", ai_response)) self.status_var.set("就绪") def add_message(self, message, sender): # 创建消息气泡 frame = ttk.Frame(self.scrollable_frame, style="Bubble.TFrame") # 添加头像名字标签 info_frame = ttk.Frame(frame) info_frame.pack(fill="x") if sender == "user": avatar = ttk.Label(info_frame, text="👤", font=("Arial", 14)) name_label = ttk.Label(info_frame, text="你", font=("Arial", 10, "bold")) avatar.pack(side="right", padx=(0, 5)) name_label.pack(side="right") label = ttk.Label(frame, text=message, style="User.TLabel") label.pack(fill="x", padx=10, pady=5) frame.pack(fill="x", padx=20, pady=5, anchor="e") elif sender == "ai": avatar = ttk.Label(info_frame, text="🤖", font=("Arial", 14)) name_label = ttk.Label(info_frame, text=self.current_ai, font=("Arial", 10, "bold")) avatar.pack(side="left", padx=(0, 5)) name_label.pack(side="left") label = ttk.Label(frame, text=message, style="AI.TLabel") label.pack(fill="x", padx=10, pady=5) frame.pack(fill="x", padx=20, pady=5, anchor="w") else: # system message label = ttk.Label(frame, text=message, font=("Arial", 10, "italic"), background="#f1c40f", foreground="#2c3e50", padding=5) label.pack(fill="x", padx=10, pady=5) frame.pack(fill="x", padx=20, pady=5) # 滚动到底部 self.canvas.yview_moveto(1.0) def clear_chat(self): # 清除所有对话 for widget in self.scrollable_frame.winfo_children(): widget.destroy() self.conversation = [] self.status_var.set("对话已清除") self.add_welcome_message() if __name__ == "__main__": root = tk.Tk() app = TextChatApp(root) root.mainloop() ``` ### 修复的问题说明: 1. **`self.current_ai` 属性缺失问题**: - 在 `__init__` 方法中增加了 `self.current_ai = random.choice(self.ai_names)` 初始化代码 - 在 `change_ai` 方法中确保正确更新 `self.current_ai` 属性 2. **初始化顺序优化**: - 确保在创建UI组件前初始化 `self.current_ai` - 将AI助手列表 `self.ai_names` 的创建放在最前面 3. **方法调用顺序调整**: - 将 `add_welcome_message()` 调用放在 `create_widgets()` 之后 - 确保所有依赖属性都已正确初始化 ### 完整功能说明: 1. **AI助手功能**: - 支持多个AI助手角色(小爱、小智、小助手、小AI) - 可随时切换不同的助手 - 助手会生成上下文相关的回复 2. **UI界面特点**: - 现代化的深色主题界面 - 彩色对话气泡区分不同角色 - 响应式布局适应不同窗口大小 - 平滑滚动体验 3. **交互功能**: - 支持文本输入发送(回车键或按钮) - 可清除对话历史 - 状态栏显示当前系统状态 - 消息包含头像发送者名称 ### 使用说明: 1. 在底部输入框输入消息 2. 按回车键或点击"发送"按钮 3. AI助手会生成回复并显示在对话区域 4. 点击"切换助手"可更换AI助手角色 5. 点击"🗑️ 清除对话"可清空对话历史 这个修复版本解决了 `self.current_ai` 属性缺失的问题,确保程序可以正常运行。现在AI助手名称会正确显示在消息气泡顶部信息栏中。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程IT圈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值