I was building a class that would work like a switch;
such custom class would have been a string, with an inner list, whereas the string identifies the current position of the switch ("on") and the list covers all the possible positions (["on","off","halfway on", "kinda dead but still on", ...];
While initializing an instance of this class, I would accept an indefinite number of possible positions;
Hence the code
class switch(str):
pos=[]
def __init__(self,*positions):
self.pos=[str(el) for el in positions]
self=self.pos[0]
def swap(self):
try:
self=self.pos[self.pos.index(self)+1]
except IndexError:
self=self.pos[0]
and the call
mood=switch("upbeat","depressed","horny")
That quite doesn't work. The error goes like this:
TypeError: decoding str is not supported
Of course there are better ways to make this Switch class (this was just a draft of a quick insertion) and eventually, I already did; still I didn't understand the origins of that error - whose search through Google wasn't useful - and angered enough about it that I finally decided to sign up here on Stack and post about it.
What do you guys think about it?
Googling it up, it seems like the error would turn off when trying to concatenate strings like "str1","str2" and not like "str1"+"str2"; then I think it's safe to keep suspicions around the *positions thing, with it not unpacking the strings as separate variables, but trying to guess I wanted them to be joined as one but failed at it as I wasn't using the + concatenator.
That's what came to my mind.
Otherwise, if I'm wrong, I've seen the error pop up only while playing with formats and stuff. Which is pretty way off in this case.
You can subclass str. But if you are passing more than a single argument, you need to intercept that at __new__() so python doesn't try to interpret the other arguments when creating the object.
Note this is just to demonstrate — it won't work for your problem
class switch(str):
def __new__(cls, *content):
return str.__new__(cls, content[0])
def __init__(self, *positions):
self.pos = [str(el) for el in positions]
mood=switch("upbeat", "depressed", "horny")
# prints as expected and has string methods
print(mood, mood.upper())
# upbeat UPBEAT
# has your instance attribute
print(mood.pos)
['upbeat', 'depressed', 'horny']
The problem is that strings are not mutable. So this is doomed from the beginning if the idea is to change the value of the string in-place. You can instead use collections.UserString for this. This acts like a string but gives you a data property to store the actual value. With this, your idea might work:
from collections import UserString
class Switch(UserString):
def __init__(self, *positions):
self.pos = [str(el) for el in positions]
# store the actual string in .data
self.data = self.pos[0]
def swap(self):
try:
self.data = self.pos[self.pos.index(self)+1]
except IndexError:
self.data = self.pos[0]
mood=Switch("upbeat", "depressed", "horny")
# still acts like a string
print(mood, mood.upper())
# upbeat UPBEAT
# but now you can swap
mood.swap()
print(mood, mood.upper())
# depressed DEPRESSED
I think Python thinks you're using the second constructor of str() (https://docs.python.org/3/library/stdtypes.html#str).
e.g. switch(b"upbeat", "utf-8") would work.
Related
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.
Hi im trying to check the inputs when a new class object is created to determine if they are input correctly and if say "theMoves" is input incorrectly then do something like ignore it or attempt at converting it to a list.
Class Card:
def __init__(self, theName,theHP, theMoves ):
self.theName=str(theName)
self.theHp=int(theHP)
self.theMoves=theMoves # [(),()..]
Class Deck:
#more code here
#When i try adding a card to the deck i get an index error because theMoves in the Card class arnt correct. It works with the c0,c1
def main():
#c0=Card("Dave",454,[("Fieball",999)]) # works
c1=Card("Bob",500,[("Fireball",999),("Flame",999),("Waterblast",499)]) #works
#c2=Card("Peter",400,(fire,342)) # Fix
#c3=Card("Josh",300,waterb,22) #fix (maybe by just ignoring the moves after checking each varible)
Im wondering if there is a way so that if someone enters the information incorrect like I have done in "c2"/"c3" then it should either convert the values to match the format like c0 or c1 or just ignore the value all together.
If its easier, I dont mind just ignore theMove value if entered incorrectly but im not sure how to go about doing it ? When i looked online i seen someone mention the new method but im not too sure with python and objects on how I would go about doing it.
Thanks for your time and help in advance:)
use isinstance():
if isinstance(theMoves, list) and theMoves and isinstance(theMoves[0], tuple):
pass
elif isinstance(theMoves, tuple):
theMoves = [theMoves]
else:
raise ValueError('Unexpected input type')
But in general programmers cannot handle all possible cases. Letting it break on unexpected input is not a bad design compared to trying to parse all inputs.
In addition to using isinstance(), you can use type hinting, which gives more syntactic sugar.
from typing import List, Tuple
def __init__(self, theName: str, theHP: int, theMoves: List[Tuple[str, int]]):
self.theName = theName
self.theHp = theHP
self.theMoves = theMoves
As you can see, the syntax is much cleaner than what you would get with isinstance().
If there is an import error, you can install/upgrade the official module with pip: pip install --upgrade typing.
The documentation can be found here.
First, if you guys think the way I'm trying to do things is not Pythonic, feel free to offer alternative suggestions.
I have an object whose functionality needs to change based on outside events. What I've been doing originally is create a new object that inherits from original (let's call it OrigObject()) and overwrites the methods that change (let's call the new object NewObject()). Then I modified both constructors such that they can take in a complete object of the other type to fill in its own values based on the passed in object. Then when I'd need to change functionality, I'd just execute myObject = NewObject(myObject).
I'm starting to see several problems with that approach now. First of all, other places that reference the object need to be updated to reference the new type as well (the above statement, for example, would only update the local myObject variable). But that's not hard to update, only annoying part is remembering to update it in other places each time I change the object in order to prevent weird program behavior.
Second, I'm noticing scenarios where I need a single method from NewObject(), but the other methods from OrigObject(), and I need to be able to switch the functionality on the fly. It doesn't seem like the best solution anymore to be using inheritance, where I'd need to make M*N different classes (where M is the number of methods the class has that can change, and N is the number of variations for each method) that inherit from OrigObject().
I was thinking of using attribute remapping instead, but I seem to be running into issues with it. For example, say I have something like this:
def hybrid_type2(someobj, a):
#do something else
...
class OrigObject(object):
...
def hybrid_fun(self, a):
#do something
...
def switch(type):
if type == 1:
self.hybrid_fun = OrigObject.hybrid_fun
else:
self.fybrid_fun = hybrid_type2
Problem is, after doing this and trying to call the new hybrid_fun after switching it, I get an error saying that hybrid_type2() takes exactly 2 arguments, but I'm passing it one. The object doesn't seem to be passing itself as an argument to the new function anymore like it does with its own methods, anything I can do to remedy that?
I tried including hybrid_type2 inside the class as well and then using self.hybrid_fun = self.hybrid_type2 works, but using self.hybrid_fun = OrigObject.hybrid_fun causes a similar error (complaining that the first argument should be of type OrigObject). I know I can instead define OrigObject.hybrid_fun() logic inside OrigObject.hybrid_type1() so I can revert it back the same way I'm setting it (relative to the instance, rather than relative to the class to avoid having object not be the first argument). But I wanted to ask here if there is a cleaner approach I'm not seeing here? Thanks
EDIT:
Thanks guys, I've given points for several of the solutions that worked well. I essentially ended up using a Strategy pattern using types.MethodType(), I've accepted the answer that explained how to do the Strategy pattern in python (the Wikipedia article was more general, and the use of interfaces is not needed in Python).
Use the types module to create an instance method for a particular instance.
eg.
import types
def strategyA(possible_self):
pass
instance = OrigObject()
instance.strategy = types.MethodType(strategyA, instance)
instance.strategy()
Note that this only effects this specific instance, no other instances will be effected.
You want the Strategy Pattern.
Read about descriptors in Python. The next code should work:
else:
self.fybrid_fun = hybrid_type2.__get__(self, OrigObject)
What about defining it like so:
def hybrid_type2(someobj, a):
#do something else
...
def hybrid_type1(someobj, a):
#do something
...
class OrigObject(object):
def __init__(self):
...
self.run_the_fun = hybrid_type1
...
def hybrid_fun(self, a):
self.run_the_fun(self, a)
def type_switch(self, type):
if type == 1:
self.run_the_fun = hybrid_type1
else:
self.run_the_fun = hybrid_type2
You can change class at runtime:
class OrigObject(object):
...
def hybrid_fun(self, a):
#do something
...
def switch(self):
self.__class__ = DerivedObject
class DerivedObject(OrigObject):
def hybrid_fun(self, a):
#do the other thing
...
def switch(self):
self.__class__ = OrigObject
I have a bunch of variables that are equal to values pulled from a database. Sometimes, the database doesn't have a value and returns "NoneType". I'm taking these variables and using them to build an XML file. When the variable is NoneType, it causes the XML value to read "None" rather than blank as I'd prefer.
My question is: Is there an efficient way to go through all the variables at once and search for a NoneType and, if found, turn it to a blank string?
ex.
from types import *
[Connection to database omitted]
color = database.color
size = database.size
shape = database.shape
name = database.name
... etc
I could obviously do something like this:
if type(color) is NoneType:
color = ""
but that would become tedious for the 15+ variables I have. Is there a more efficient way to go through and check each variable for it's type and then correct it, if necessary? Something like creating a function to do the check/correction and having an automated way of passing each variable through that function?
All the solutions given here will make your code shorter and less tedious, but if you really have a lot of variables I think you will appreciate this, since it won't make you add even a single extra character of code for each variable:
class NoneWrapper(object):
def __init__(self, wrapped):
self.wrapped = wrapped
def __getattr__(self, name):
value = getattr(self.wrapped, name)
if value is None:
return ''
else:
return value
mydb = NoneWrapper(database)
color = mydb.color
size = mydb.size
shape = mydb.shape
name = mydb.name
# All of these will be set to an empty string if their
# original value in the database is none
Edit
I thought it was obvious, but I keep forgetting it takes time until all the fun Python magickery becomes a second nature. :) So how NoneWrapper does its magic? It's very simple, really. Each python class can define some "special" methods names that are easy to identify, because they are always surrounded by two underscores from each side. The most common and well-known of these methods is __init__(), which initializes each instance of the class, but there are many other useful special methods, and one of them is __getattr__(). This method is called whenever someone tries to access an attribute. of an instance of your class, and you can customize it to customize attribute access.
What NoneWrapper does is to override getattr, so whenever someone tries to read an attribute of mydb (which is a NoneWrapper instance), it reads the attribute with the specified name from the wrapped object (in this case, database) and return it - unless it's value is None, in which case it returns an empty string.
I should add here that both object variables and methods are attributes, and, in fact, for Python they are essentially the same thing: all attributes are variables that could be changed, and methods just happen to be variables that have their value set to a function of special type (bound method). So you can also use getattr() to control access to functions, which could lead to many interesting uses.
The way I would do it, although I don't know if it is the best, would be to put the variables you want to check and then use a for statement to iterate through the list.
check_vars = [color,size,shape,name]
for var in check_vars:
if type(var) is NoneType:
var = ""
To add variables all you have to do is add them to the list.
If you're already getting them one at a time, it's not that much longer to write:
def none_to_blank(value):
if value is None:
return ""
return value
color = none_to_blank(database.color)
size = none_to_blank(database.size)
shape = none_to_blank(database.shape)
name = none_to_blank(database.name)
Incidentally, use of "import *" is generally discouraged. Import only what you're using.
you can simply use:
color = database.color or ""
another way is to use a function:
def filter_None(var):
"" if (a is None) else a
color = filter_None(database.color)
I don't know how the database object is structured but another solution is to modify the database object like:
def myget(self, varname):
value = self.__dict__[varname]
return "" if (value is None) else value
DataBase.myget = myget
database = DataBase(...)
[...]
color = database.myget("color")
you can do better using descriptors or properties
Basically, I have a list like: [START, 'foo', 'bar', 'spam', eggs', END] and the START/END identifiers are necessary for later so I can compare later on. Right now, I have it set up like this:
START = object()
END = object()
This works fine, but it suffers from the problem of not working with pickling. I tried doing it the following way, but it seems like a terrible method of accomplishing this:
class START(object):pass
class END(object):pass
Could anybody share a better means of doing this? Also, the example I have set up above is just an oversimplification of a different problem.
If you want an object that's guaranteed to be unique and can also be guaranteed to get restored to exactly the same identify if pickled and unpickled right back, top-level functions, classes, class instances, and if you care about is rather than == also lists (and other mutables), are all fine. I.e., any of:
# work for == as well as is
class START(object): pass
def START(): pass
class Whatever(object): pass
START = Whatever()
# if you don't care for "accidental" == and only check with `is`
START = []
START = {}
START = set()
None of these is terrible, none has any special advantage (depending if you care about == or just is). Probably def wins by dint of generality, conciseness, and lighter weight.
You can define a Symbol class for handling START and END.
class Symbol:
def __init__(self, value):
self.value = value
def __eq__(self, other):
return isinstance(other, Symbol) and other.value == self.value
def __repr__(self):
return "<sym: %r>" % self.value
def __str__(self):
return str(self.value)
START = Symbol("START")
END = Symbol("END")
# test pickle
import pickle
assert START == pickle.loads(pickle.dumps(START))
assert END == pickle.loads(pickle.dumps(END))
Actually, I like your solution.
A while back I was hacking on a Python module, and I wanted to have a special magical value that could not appear anywhere else. I spent some time thinking about it and the best I came up with is the same trick you used: declare a class, and use the class object as the special magical value.
When you are checking for the sentinel, you should of course use the is operator, for object identity:
for x in my_list:
if x is START:
# handle start of list
elif x is END:
# handle end of list
else:
# handle item from list
If your list didn't have strings, I'd just use "start", "end" as Python makes the comparison O(1) due to interning.
If you do need strings, but not tuples, the complete cheapskate method is:
[("START",), 'foo', 'bar', 'spam', eggs', ("END",)]
PS: I was sure your list was numbers before, not strings, but I can't see any revisions so I must have imagined it
I think maybe this would be easier to answer if you were more explicit about what you need this for, but my inclination if faced with a problem like this would be something like:
>>> START = os.urandom(16).encode('hex')
>>> END = os.urandom(16).encode('hex')
Pros of this approach, as I'm seeing it
Your markers are strings (can pickle or otherwise easily serialize, eg to JSON or a DB, without any special effort)
Very unlikely to collide either accidentally or on purpose
Will serialize and deserialize to identical values, even across process restarts, which (I think) would not be the case for object() or an empty class.
Cons(?)
Each time they are newly chosen they will be completely different. (This being good or bad depends on details you have not provided, I would think).