TL/DR: Only how you perceive your work matters really. Don't be scared of learning to find solutions. If you see something missing, it might mean you were meant to create it. Class Dunder tutorial at the end.
I bet most of you here had to go through this type of thought process to motivate you to keep pushing even when everything seems unachievable. Sometime it's because you took on something that was more complex than you previously thought or while fixing an issue, you end up with a bigger problem to figure out than a simple bug fix. Those surprise challenges that appears on the path often drives the best of us to self-torture for not being able to figure it out in a timely manner that prevents peers from pressuring for a solution.
Always remember, you are okay. Not all solution can be found immediately, even if the unrealistic expectation of others are looming over your shoulder. Let them keep expecting unreal results and be disappointed. Did you try to fix it? If the answer if yes then you have more to be proud about it than anyone complaining about any new problems that appears.
Personnally, I'm starting to find that my code_checker project has put me in this situation many times, forcing me to slow down and learn the intricacies of python. I recomfort myself in knowning that no matter what problem I face, there's a solution. Might not be an immediately found solution, but the possibility of a problem being solved is way higher nowadays compared to when I started learning coding in the late 1990.
We are in an era where coding can be as pretty and efficient as it could ever be, we just need to focus on creating it and making it accessible. How many of you have complaints about a specific language just because it's missing parts of another language that you like? I know that I constantly have to pre-patch any language that I want to use to make it behave in a way that feels more comfortable for a HUMAN to use.
I do think we are about to experience a moment in computer science where coding will have two distinct version, the low-level machine coding and the high-level human applications. Although some humans do like to code low-level language, most of the synthax is unreadable without highly advanced knowledge of that language (usually C or Rust) which still would categorize it as more adapted for machine to handle than humans. There's a reason why many of us gives this level of importance to what is arguably and subjectively called clean code.
Although the specifics of clean code can be slightly different between individuals, the overall objective of using clean code should always be the same: easy to read and understand. I always thought of any attempts at recreating an algorithmic synthax as attempting to create clean code. I am currently working on a workaround meant specifically for cleaning and standardizing python class objects usage and output. Here's an example of a big flaw of class object in python; the lack of native support for them:
π’ Summary
Operation
Foo + Foo
Normal Class (class Foo)
β TypeError (no __add__)
With Dunders / Meta
β
if metaclass defines __add__
Operation
Foo(β¦) + Foo(β¦) (instances)
Normal Class (class Foo)
β TypeError (no __add__)
β
if instance defines __add__
Operation
Foo[0]
Normal Class (class Foo)
β TypeError (not subscriptable)
With Dunders / Meta
β
if metaclass defines __getitem__
Operation
Foo_instance(β¦)
Normal Class (class Foo)
β TypeError (not callable)
With Dunders / Meta
β
if instance defines __call__
So I learned how Python works and most of my software do exactly what I want them to do, that is until I started using objects. Then it became a dunder hunting nightmare as each time I tried to use an object in a new scenario, it would always end up failing because of the lack of support for them.
And thus Magic Pythong was born, an honest attempt at making class objects more usable natively through a simple package/module import. I'm still gathering any possible feedback about already existing libraries that does similar stuff but so far nothing. This specific "patch" is what I will identify officially as an UniversalObject class, a class that has all missing native operation support and it's accessible from the scope it is imported into.
Want to use a boolean check with two class stored value? you now can. Want to addition two int value stored in two objects? you now can. Want to get the type of a value stored into an object? it will now give you the proper type instead of always outputing type. Want to create an actual constant value that is protected? you bet you can now. Why was those options not already available? I have no idea but I bet it has to do with Python Interpreter.
This UniversalObject class is actually ready for use and collaborative work. Before I attempt any opening of my code publicly, I want to learn how to do it safely. Many article I've read about people accidently exposing their API keys because they use vibe coding in their project management. So until I feel comfortable opening it, I'll keep learning how to properly do it and create more workarounds that I want.
This is when I thought about java. Yes java, ugh. So I thought about locking an object for access. It's somewhere in your code but it needs a key to be used. It reminded me of the private / public function system that java supports and felt it could be a great addition to have if ever I wanted to start creating video games with secret levels. This was the birth of my bane; LocalObject.
This is thing is a pain to create, not only is it hard to create the workaround to make class usable natively, adding a lock on each of those workaround to avoid data leak is extremely frustrating. the getattr and setattr dunders just dont want to play nice with me at all and they got me stuck into testing different approach for a whole week.
So more learning it is, decided to get a closer look at them, and again, using my favorite GPT friend to produce me some decent starting points for researching. I'm using these posts to share the data I'm extracting so that if ever you feel it's something you could use to learn, then I'm glad I helped you.
π Python Class Dunder Hacks: A Full Tutorial
Dunder (double underscore) methods are hooks Python calls automatically. Theyβre not βmagic,β but they feel like it because you never call them directly β Python does.
1. Object Lifecycle
These dunders control how objects are created and destroyed.
__new__(cls, *args, **kwargs)
Called before __init__. Returns the actual instance.
Hack: implement singletons or immutable types.
class OnlyOne:
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
__init__(self, ...)
Normal initializer.
__del__(self)
Destructor (called when garbage-collected). Rarely reliable.
2. Attribute Control
These dunders let you intercept attribute access like a proxy.
__getattr__(self, name)
Called when attribute name doesnβt exist.
class Lazy:
def __getattr__(self, name):
return f"Generated {name} dynamically"
__getattribute__(self, name)
Called always for every attribute access. (Be careful: must avoid recursion by using object.__getattribute__).
class Spy:
def __getattribute__(self, name):
print(f"Accessed {name}")
return object.__getattribute__(self, name)
__setattr__(self, name, value)
Intercepts obj.attr = value.
class Locked:
def __setattr__(self, name, value):
if name.startswith("_"):
object.__setattr__(self, name, value)
else:
raise TypeError("Locked object: cannot set attributes")
__delattr__(self, name)
Called on del obj.attr.
__dir__(self)
Customizes dir(obj) output.
3. Representation & Conversion
These dunders make your objects look nice.
__repr__(self) β unambiguous string (for devs).
__str__(self) β pretty string (for users).
__bytes__(self) β bytes(obj).
__format__(self, spec) β f-string formatting.
class Person:
def __init__(self, name):
self.name = name
def __repr__(self):
return f"Person({self.name!r})"
def __str__(self):
return self.name
4. Container Emulation
Make objects act like lists, dicts, sets.
__len__(self) β len(obj)
__getitem__(self, key) β obj[key]
__setitem__(self, key, value) β obj[key] = value
__delitem__(self, key) β del obj[key]
__iter__(self) β for x in obj:
__contains__(self, item) β item in obj
class VirtualList:
def __getitem__(self, idx):
return idx * 10
def __len__(self):
return 999
5. Callable & Context Manager
__call__(self, *args, **kwargs) β make an object act like a function.
class Adder:
def __call__(self, a, b):
return a + b
__enter__ / __exit__ β context managers (with statement).
class TempFile:
def __enter__(self): print("OPEN")
def __exit__(self, exc_type, exc_val, exc_tb): print("CLOSE")
6. Operator Overloading
Arithmetic dunders let objects behave like numbers.
__add__(self, other) β +
__sub__ β -
__mul__ β *
__truediv__ β /
__floordiv__ β //
__mod__ β %
__pow__ β **
__radd__, __rmul__, etc. β reverse ops
__eq__, __lt__, __gt__, __ne__, etc. β comparisons
__hash__ β hash(obj)
class Vec2:
def __init__(self, x, y): self.x, self.y = x, y
def __add__(self, o): return Vec2(self.x+o.x, self.y+o.y)
def __repr__(self): return f"Vec2({self.x},{self.y})"
7. Class Control
Dunders at the class level.
__class_getitem__(cls, key) β allow generics (MyClass[int]).
__instancecheck__ / __subclasscheck__ β customize isinstance/issubclass.
__init_subclass__ β hook called when subclassing.
__mro_entries__ β control multiple inheritance resolution.
__prepare__ (in metaclasses) β customize class dict before creation.
class Meta(type):
def __new__(mcls, name, bases, dct):
print(f"Creating {name}")
return super().__new__(mcls, name, bases, dct)
class MyClass(metaclass=Meta): pass
8. Pickling & Copying
__getstate__ / __setstate__ β for pickle.
__reduce__ β advanced pickling control.
__copy__, __deepcopy__ β for copy module.
9. Async & Awaitable
__await__ β await obj
__aiter__, __anext__ β async iteration
__aenter__, __aexit__ β async context manager.
class AsyncCounter:
def __aiter__(self): self.i = 0; return self
async def __anext__(self):
self.i += 1
if self.i > 3: raise StopAsyncIteration
return self.i
𧨠Real Hacks You Can Pull Off
Sealed objects β lock attributes with __setattr__.
Lazy loading β __getattr__ to fetch things on demand.
Immutable data classes β override __setattr__ to block changes.
Fluent DSLs β __call__ + __getitem__ to make objects behave like mini-languages.
Proxy objects β wrap APIs with logging via __getattribute__.
Sandboxing β disallow dangerous attributes dynamically.
This is where I'm currently working on, learning those dunder hacks properly so I can start working together with the agent on the fix. I usually work in the testing lab instead of assisting the agent and simply do vibe coding cleanup once all the premade test passes. This is how I could identify that the locking mechanism was being skipped instead of refactored.
Top comments (0)