我正在开发一个 Python 项目,在其中计算并绘制等分序列图。我创建了一个 list.py 脚本,用于显示 .db 文件中的数字,结果证明这些数字已正确计算并保存到数据库中。但是,当我尝试使用 graph.py 绘制序列时,遇到以下错误:
Exception in Tkinter callback
AttributeError: 'int' object has no attribute 'log'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "C:\Python312\Lib\tkinter\__init__.py", line 1967, in __call__
return self.func(*args)
^^^^^^^^^^^^^^^^
File "graph.py", line 94, in plot_graph
plot_sequence(sequence, log_scale=True, log_base=log_base)
File "graph.py", line 35, in plot_sequence
transformed_sequence = np.log(sequence) / np.log(log_base)
^^^^^^^^^^^^^^^^
TypeError: loop of ufunc does not support argument 0 of type int which has no callable log method
这是 graph.py 脚本:
import os
import sqlite3
import tkinter as tk
from tkinter import filedialog, messagebox
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
def fetch_sequence_from_db(db_path):
try:
conn = sqlite3.connect(db_path)
c = conn.cursor()
c.execute("SELECT sequence FROM sequences")
sequence_str = c.fetchone()[0]
sequence = [int(x) for x in sequence_str.strip('][').split(', ')]
conn.close()
return sequence
except Exception as e:
messagebox.showerror("Error", f"Failed to read database: {e}")
return None
def format_scientific(number):
exponent = int(np.log10(number))
base = number / 10**exponent
return f"{base:.1f} x 10^{exponent}"
def plot_sequence(sequence, log_scale=False, log_base=None):
fig, ax = plt.subplots(figsize=(10, 5))
if log_scale and log_base:
transformed_sequence = np.log(sequence) / np.log(log_base)
ylabel = f"log_{log_base}(Number)"
else:
transformed_sequence = sequence
ylabel = "Number"
ax.set_title("Aliquot Sequence")
ax.set_xlabel("Iteration")
ax.set_ylabel(ylabel)
ax.grid(True)
ax.set_xlim(0, len(transformed_sequence) - 1)
line, = ax.plot([], [], linestyle='-', color='b')
def update(frame):
line.set_data(range(frame + 1), transformed_sequence[:frame + 1])
ax.relim()
ax.autoscale_view()
return line,
ani = FuncAnimation(fig, update, frames=len(transformed_sequence), blit=True, interval=10, repeat=False)
# Find and format the largest number
largest_number = max(sequence)
formatted_largest_number = format_scientific(largest_number)
# Display the largest number on the graph
plt.text(0.5, 1.05, f"Largest number: {formatted_largest_number}", ha='center', va='center', transform=ax.transAxes, fontsize=12)
plt.subplots_adjust(left=0.05, right=0.95) # Adjusting the left margin to make the 0th iteration close to the border
plt.show()
def select_file():
file_path = filedialog.askopenfilename(
initialdir=os.path.join(os.path.dirname(os.path.abspath(__file__)), "Number_Sequences"),
title="Select a Database File",
filetypes=(("Database Files", "*.db"), ("All Files", "*.*"))
)
if file_path:
entry_file_path.delete(0, tk.END)
entry_file_path.insert(0, file_path)
def plot_graph():
db_path = entry_file_path.get()
if not os.path.exists(db_path):
messagebox.showerror("Error", "File not found.")
return
sequence = fetch_sequence_from_db(db_path)
if not sequence:
return
if var_log_transform.get():
try:
log_base = float(entry_log_base.get())
except ValueError:
messagebox.showerror("Error", "Invalid log base.")
return
plot_sequence(sequence, log_scale=True, log_base=log_base)
else:
plot_sequence(sequence, log_scale=False)
# Setting up the Tkinter window
root = tk.Tk()
root.title("Aliquot Sequence Grapher")
frame = tk.Frame(root)
frame.pack(padx=10, pady=10)
label_file_path = tk.Label(frame, text="Database File:")
label_file_path.grid(row=0, column=0, sticky="e")
entry_file_path = tk.Entry(frame, width=40)
entry_file_path.grid(row=0, column=1, padx=5)
button_browse = tk.Button(frame, text="Browse", command=select_file)
button_browse.grid(row=0, column=2, padx=5)
var_log_transform = tk.BooleanVar(value=True)
check_log_transform = tk.Checkbutton(frame, text="Log Transform", variable=var_log_transform)
check_log_transform.grid(row=1, column=0, columnspan=3)
label_log_base = tk.Label(frame, text="Log Base:")
label_log_base.grid(row=2, column=0, sticky="e")
entry_log_base = tk.Entry(frame, width=10)
entry_log_base.grid(row=2, column=1, padx=5, sticky="w")
entry_log_base.insert(0, "10")
button_plot = tk.Button(frame, text="Plot Graph", command=plot_graph)
button_plot.grid(row=3, column=0, columnspan=3, pady=10)
root.mainloop()
这是 list.py 中的数字列表。迭代次数仅在 list.py 脚本中显示:
1 : 276
2 : 396
3 : 696
4 : 1104
5 : 1872
6 : 3770
7 : 3790
8 : 3050
9 : 2716
10 : 2772
11 : 5964
12 : 10164
13 : 19628
14 : 19684
15 : 22876
16 : 26404
17 : 30044
18 : 33796
19 : 38780
20 : 54628
21 : 54684
22 : 111300
23 : 263676
24 : 465668
25 : 465724
26 : 465780
27 : 1026060
28 : 2325540
29 : 5335260
30 : 11738916
31 : 23117724
32 : 45956820
33 : 121129260
34 : 266485716
35 : 558454764
36 : 1092873236
37 : 1470806764
38 : 1471882804
39 : 1642613196
40 : 2737688884
41 : 2740114636
42 : 2791337780
43 : 4946860492
44 : 4946860548
45 : 9344070652
46 : 9344070708
47 : 15573451404
48 : 27078171764
49 : 27284104204
50 : 27410152084
51 : 27410152140
52 : 76787720100
53 : 220578719452
54 : 254903331620
55 : 361672366300
56 : 603062136740
57 : 921203207260
58 : 1381419996068
59 : 1395444575644
60 : 1395478688996
61 : 1395546402460
62 : 2069258468900
63 : 3065057872156
64 : 3277068463844
65 : 3429776547484
66 : 3597527970596
67 : 4028517592540
68 : 5641400009252
69 : 5641400009308
70 : 5641400009364
71 : 9709348326636
72 : 16331909651988
73 : 31948891146732
74 : 54770416120644
75 : 100509779504316
76 : 208751080955844
77 : 388416032284476
78 : 749365894850244
79 : 1414070378301756
80 : 2556878765995204
81 : 2556878765995260
82 : 6726041614128900
83 : 15626498692840700
84 : 23762659088671300
85 : 35168735451235260
86 : 78257359358590020
87 : 186897487211247036
88 : 340813223900632644
89 : 592585414385033916
90 : 1326583294186844484
91 : 2594892903616159356
92 : 4946738730471899844
93 : 8244565422068579772
94 : 13740942370114299844
95 : 13780400058385352252
96 : 13780400058385352308
97 : 14272557426581383244
98 : 14272557426581383300
99 : 21155073391000330684
100 : 21374326697892540932
101 : 22138822441861473292
我也有相同的文件,但只有 70 次迭代且数字相同,并且它绘制的图表很好。这是 main.py 脚本,用于计算等分序列(如果需要):
import os
import math
import sqlite3
# Function to get all divisors of a given number
def get_divisors(n):
divisors = [1]
for i in range(2, int(math.sqrt(n)) + 1):
if n % i == 0:
divisors.append(i)
if i != n // i:
divisors.append(n // i)
if n > 1:
divisors.append(n)
return sorted(divisors)
# Function to calculate the sum of divisors for a given number
def sum_of_divisors(n):
return sum(get_divisors(n)) - n
# Function to calculate the Aliquot Sequence for a given number
def aliquot_sequence(start_number, iterations=None):
sequence = [start_number]
current_iteration = 1
while True:
next_number = sum_of_divisors(sequence[-1])
sequence.append(next_number)
print(f"Iteration {current_iteration}: {next_number}")
if next_number == 1 or next_number in sequence[:-1] or next_number == sum_of_divisors(next_number):
break
if iterations and current_iteration >= iterations:
break
current_iteration += 1
return sequence
# Function to save the result to the database
def save_to_database(number, sequence, iterations=None, continue_from_file=None):
current_dir = os.path.dirname(os.path.abspath(__file__))
folder_name = os.path.join(current_dir, "Number Sequences")
if not os.path.exists(folder_name):
try:
os.makedirs(folder_name)
except OSError as e:
print(f"Error creating folder: {e}")
return
if continue_from_file:
db_file_path = continue_from_file
original_name, _ = os.path.splitext(os.path.basename(continue_from_file))
original_number, original_iterations = original_name.split('_')
original_iterations = int(original_iterations)
total_iterations = original_iterations + (len(sequence) - 1)
if sequence[-1] == 1 or sequence[-1] in sequence[:-1] or sequence[-1] == sum_of_divisors(sequence[-1]):
final_file_name = f"{number}.db"
else:
final_file_name = f"{number}_{total_iterations}.db"
final_db_file_path = os.path.join(folder_name, final_file_name)
else:
if iterations:
file_name = f"{number}_{iterations}.db"
else:
file_name = f"{number}.db"
db_file_path = os.path.join(folder_name, file_name)
try:
conn = sqlite3.connect(db_file_path)
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS sequences (number INTEGER, sequence TEXT)''')
if continue_from_file:
c.execute('''SELECT sequence FROM sequences''')
old_sequence_str = c.fetchone()[0]
old_sequence = [int(x) for x in old_sequence_str.strip('][').split(', ')]
combined_sequence = old_sequence + sequence[1:] # Exclude the initial number from new sequence
c.execute('''UPDATE sequences SET sequence = ? WHERE number = ?''', (str(combined_sequence), number))
else:
c.execute('''INSERT INTO sequences (number, sequence) VALUES (?, ?)''', (number, str(sequence)))
conn.commit()
conn.close()
if continue_from_file and db_file_path != final_db_file_path:
try:
os.rename(db_file_path, final_db_file_path)
except OSError as e:
print(f"Error renaming file to final name: {e}")
except sqlite3.Error as e:
print(f"Database error: {e}")
return
# Function to check if number exists in the database
def number_exists_in_database(number, iterations=None):
current_dir = os.path.dirname(os.path.abspath(__file__))
folder_name = os.path.join(current_dir, "Number Sequences")
if os.path.exists(folder_name):
for file in os.listdir(folder_name):
if file.endswith(".db"):
db_name, _ = os.path.splitext(file)
split_name = db_name.split('_')
db_number = int(split_name[0])
db_iterations = None if len(split_name) == 1 else int(split_name[1])
if db_number == number and db_iterations == iterations:
return True
return False
# Main function
def main():
# Get input from the user
number = int(input("Enter a number to calculate its Aliquot Sequence: "))
iterations = input("Enter 'auto' to calculate until stopped automatically, or enter the number of iterations: ")
if iterations.lower() == 'auto':
iterations = None
else:
iterations = int(iterations)
continue_calculation = input("Do you want to continue calculation from where you left? (yes/no): ")
if continue_calculation.lower() == 'yes':
continue_from_file = input("Enter the file name to continue from: ")
continue_from_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "Number Sequences", continue_from_file)
if not os.path.exists(continue_from_file):
print("File not found.")
return
conn = sqlite3.connect(continue_from_file)
c = conn.cursor()
c.execute('''SELECT sequence FROM sequences''')
sequence_str = c.fetchone()[0]
sequence = [int(x) for x in sequence_str.strip('][').split(', ')]
conn.close()
start_number = sequence[-1] # Start from the last number in the sequence
else:
continue_from_file = None
start_number = number
# Check if number exists in the database
if number_exists_in_database(number, iterations):
print("Sequence already exists in the database.")
return
# Calculate Aliquot Sequence
new_sequence = aliquot_sequence(start_number, iterations)
# Print the sequence
print(f"Aliquot Sequence for {number}: {new_sequence}")
# Save the sequence to the database
save_to_database(number, new_sequence, iterations, continue_from_file)
print("Sequence saved to database.")
if __name__ == "__main__":
main()
确保log_base在使用前转换为float。
问题始于 fetch_sequence_from_db 函数。 try 块失败,异常返回 None 作为序列值(我相信您的消息框中应该有错误)。
从您的代码来看,您似乎是通过拆分连接数组来生成序列值的:
sequence = [int(x) for x in sequence_str.strip('][').split(', ')]
但是如果您的sequence_str值为'[2, 3, 4][4, 5, 6]',那么您将删除']['并将外部括号留在字符串'[2, 3, 44, 5, 6中]'。 您可以看到,用逗号分割将生成一个列表,其中字符串“[2”和“6]”作为第一个和最后一个值。和中间的字符串“44”。 第一个字符串将使尝试失败,因此,它将返回 None 作为值,并且 np.log(None) 生成您得到的错误。
我建议:
sequence = [int(x) for x in sequence_str[1:-1].replace('][', ', ').split(', ')]