Duck Typing in general is explained here: https://stackoverflow.com/a/4205163/19446851.
What does Duck Typing mean in Python? Is it really possible to make one type look like another type. Can I have an own class that "looks and quacks" like a string?
See the following example:
from dataclasses import dataclass
@dataclass
class ColoredObject:
color : ...
name : ...
def __str__(self):
return self.color + " " + self.name
x = ColoredObject("red", "circle")
print("I have a " + x + ".")
That code does not work because strings and objects of the type ColoredObject cannot be concatenated. If in Python it would actually be possible to make ColoredObject "look and quack" like a string, there should be a way to concatenate both without the explicit conversion.
The following is a more practical example. I try to make the class MutableText "looking and quacking" like a string so that I can use it in an XML Element Tree.
import xml.etree.cElementTree as ET
root = ET.Element("root_node")
class MutableText:
def __init__(self, init_text):
self.text = init_text
mutable_contents = MutableText("ZigZag")
ET.SubElement(root, "child_node").text = mutable_contents
tree = ET.ElementTree(root)
tree.write("filename.xml")
The goal is that line ET.SubElement(root, "child_node").text = mutable_contents
works. What can I do to achieve this?
The error message, that I get with the code is TypeError: cannot serialize <__main__.MutableText object at 0x7fafc0099e20> (type MutableText)
I already got the advice to inherit from str class. But this is not Duck Typing. This is static typing like in C++ or Java.
Another advice to use ET.SubElement(root, "child_node").text = mutable_contents.text
is good. But that is also not Duck Typing. And that means, I always have to update the ElementTree whenever mutable_contents changes. (This is actually my motivation, why I ask this academic question. I am trying to find a solution for not having to always do this update.)
I also got the comment that ElementTree actually expects a string and not a MutableString. But why do people then say, Python uses Duck Typing? And why don't I get the error Message that a string is expected where a MutableString is provided?
Obviously there is something missing in my code in order to make MutableText like a string? But what is missing? And shouldn't Python give me an error message when it tries to call something from MutableText, which is missing?
Is it really possible to make one type look like another type?
This is quite typical of people who come from a statically typed language to interpret duck typing but it misses a significant aspect of the whole deal: it isn't that you are faking another type it is that your code relies on behaviour instead of types.
say we have this function:
def example_math_equation(a,b):
return a + 4*b
This doesn't dictate anything about what types a
or b
have to be, just that it should be valid to multiply b
by an integer and that a
can be added to the result. as such this code would be applicable to not just numbers but also sequences:
>>> example_math_equation("foo", "bar")
'foobarbarbarbar'
This is the idea of duck typing, that you avoid checking for types of data as much as possible and just assume they support the operations you need and if they don't you get an error. Then if someone wants to make a new data type - not with the intent to mimic another well defined data type but just to behave differently - then it could be used instead.
If you don't want to do duck typing, you just want to cheat and mimic the str class there is a route that exists:
class MockStr:
def __init__(self, initial_text):
self._TEXT = initial_text
def make_wrapper_method(methodname):
"makes a method that will forward to ._TEXT field"
def wrapper_method(self, *args, **kw):
#print("CALLED WRAPPER", methodname)
return getattr(self._TEXT, methodname)(*args, **kw)
return wrapper_method
for methodname, underlying_method in vars(str).items():
if not callable(underlying_method) or methodname in dir(MockStr):
continue
setattr(MockStr, methodname, make_wrapper_method(methodname))
x = MockStr("hi there")
print(x + " got added to a string")
But don't go down this route, the first issue you will come across is that because str
can't be added to any other built in type it doesn't bother defining a __radd__
so "a" + x
will fail unless you do that yourself, but more specifically if your goal is to make a mutable string you truely shouldn't do this because your object won't be immutable
If a class defines mutable objects and implements an
__eq__()
method, it should not implement__hash__()
, since the implementation of hashable collections requires that a key’s hash value is immutable
and if the library you are using expects the strings to be immutable and makes certain optimisations based on that then the whole journey of trying to accomplish that will just be a wild goose chase, you are much better off to learn what behaviours (methods) the library is expecting and see if you can reasonably provide those with a data type that also has the behaviour you want.