Note: In the future, you should probably post a snippet of your code that you have the most doubts about or that is typical of your code, then provide a link to the rest. All good though!
Overall, your code is pretty good. It seems to work well, and it's well-formatted; I especially like your liberal use of type hints. I don't understand the reasoning behind the .line
system, but that's beside the point.
In gateobject.py
and simulator.py
, you eval
arbitrary user text. This is bad practice (though in this case, the fact that self.type
is uppercased minimizes the potential security risk). If the user inputs an invalid gate, it will give an ugly error like this:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
NameError: name 'BADGATE' is not defined
Instead, try using a dictionary (like operators = {'OR': OR, 'AND': AND...}
); then, if self.type not in operators
, you can handle the error from there (print an error message and exit
, raise
a custom error, etc.). This will make your code (a bit) cleaner, error handling/messages (somewhat) nicer, and your program (slightly) faster.
You could also make a helper function (like def eval_op(op: str, arg1: str, arg2: str) -> str
) that handles that for you.
In several places, you import other files using extremely short names like s
, ps
or g
. Generally, you want to avoid this, as it can lead to confusion and overlaps with variables in your code. The names aren't particularly long, so you could leave them as-is.
You often use enumerate
, which is good! However, you specify enumerate(x 0)
, when the default is to start at 0 anyways. You can just leave it at enumerate(x)
.
In simulator.py:15-16
, you count the variable p_level
from 0 to len(self.circuit)
, incrementing it only at the end of each loop. This is better expressed with for p_level in range(0, len(self.circuit))
On simulator.py:18-21
, you use the names curr_ids
and curr_vals
for ids and vlaues that are, according to your comment, not current. True, they are the ones you are currently working with, but you might want to rename them to avoid confusion.
On simulator.py:49-50
, you do gate.out.value = eval(gate.type)(gate.in1.value, gate.in2.value)
, and again later without the second argument. You should probably separate this into a method on Gate
, such as setOutput()
or evalInputs()
.
On simulator.py:53
, you eval(gate.type)(...)
even though you know that gate.type == "NOT"
, and can just do NOT(...)
.
Throughout the whole program, you use "1"
and "0"
to denote true and false. If instead you replaced those with True
and False
, it will do two things: first, it would simplify your logicops.py
functions (i.e. NOT(a: bool) -> bool: !a
). Second, it would probably make your program run somewhat faster and/or use somewhat less space, since it's not dealing with strings, just booleans.
All in all, I would recommend you look for simpler ways to represent your data. You seem to be handy with the concepts, and effective at implementing them; you just need to simplify your code with builtins like for
and bool
. Please give me feedback on my feedback, and happy programming!