Under PyQt I'm timing Qt Events and I want to write a human readable string name with the time. With PySide2 just calling str() gives me the the string name but with PyQt5 it returns a string that's the enum value:
# PySide2
>>> str(QEvent.KeyPress)
'PySide2.QtCore.QEvent.Type.KeyPress'
# PyQt5
>>> str(QEvent.KeyPress)
'6'
Is there a way to get the string name using PyQt5? I can post a solution building a lookup table on startup, that works, but wondering if there is a direct way.
This is what I did to log the events happening on my QWidget subclass. Just made the lookup table from the docs https://doc.qt.io/qt-5/qevent.html
def event(self, event: QtCore.QEvent) -> bool:
print(event_lookup[str(event.type())])
return super().event(event)
event_lookup = {"0": "QEvent::None",
"114": "QEvent::ActionAdded",
"113": "QEvent::ActionChanged",
"115": "QEvent::ActionRemoved",
"99": "QEvent::ActivationChange",
"121": "QEvent::ApplicationActivate",
"122": "QEvent::ApplicationDeactivate",
"36": "QEvent::ApplicationFontChange",
"37": "QEvent::ApplicationLayoutDirectionChange",
"38": "QEvent::ApplicationPaletteChange",
"214": "QEvent::ApplicationStateChange",
"35": "QEvent::ApplicationWindowIconChange",
"68": "QEvent::ChildAdded",
"69": "QEvent::ChildPolished",
"71": "QEvent::ChildRemoved",
"40": "QEvent::Clipboard",
"19": "QEvent::Close",
"200": "QEvent::CloseSoftwareInputPanel",
"178": "QEvent::ContentsRectChange",
"82": "QEvent::ContextMenu",
"183": "QEvent::CursorChange",
"52": "QEvent::DeferredDelete",
"60": "QEvent::DragEnter",
"62": "QEvent::DragLeave",
"61": "QEvent::DragMove",
"63": "QEvent::Drop",
"170": "QEvent::DynamicPropertyChange",
"98": "QEvent::EnabledChange",
"10": "QEvent::Enter",
"150": "QEvent::EnterEditFocus",
"124": "QEvent::EnterWhatsThisMode",
"206": "QEvent::Expose",
"116": "QEvent::FileOpen",
"8": "QEvent::FocusIn",
"9": "QEvent::FocusOut",
"23": "QEvent::FocusAboutToChange",
"97": "QEvent::FontChange",
"198": "QEvent::Gesture",
"202": "QEvent::GestureOverride",
"188": "QEvent::GrabKeyboard",
"186": "QEvent::GrabMouse",
"159": "QEvent::GraphicsSceneContextMenu",
"164": "QEvent::GraphicsSceneDragEnter",
"166": "QEvent::GraphicsSceneDragLeave",
"165": "QEvent::GraphicsSceneDragMove",
"167": "QEvent::GraphicsSceneDrop",
"163": "QEvent::GraphicsSceneHelp",
"160": "QEvent::GraphicsSceneHoverEnter",
"162": "QEvent::GraphicsSceneHoverLeave",
"161": "QEvent::GraphicsSceneHoverMove",
"158": "QEvent::GraphicsSceneMouseDoubleClick",
"155": "QEvent::GraphicsSceneMouseMove",
"156": "QEvent::GraphicsSceneMousePress",
"157": "QEvent::GraphicsSceneMouseRelease",
"182": "QEvent::GraphicsSceneMove",
"181": "QEvent::GraphicsSceneResize",
"168": "QEvent::GraphicsSceneWheel",
"18": "QEvent::Hide",
"27": "QEvent::HideToParent",
"127": "QEvent::HoverEnter",
"128": "QEvent::HoverLeave",
"129": "QEvent::HoverMove",
"96": "QEvent::IconDrag",
"101": "QEvent::IconTextChange",
"83": "QEvent::InputMethod",
"207": "QEvent::InputMethodQuery",
"169": "QEvent::KeyboardLayoutChange",
"6": "QEvent::KeyPress",
"7": "QEvent::KeyRelease",
"89": "QEvent::LanguageChange",
"90": "QEvent::LayoutDirectionChange",
"76": "QEvent::LayoutRequest",
"11": "QEvent::Leave",
"151": "QEvent::LeaveEditFocus",
"125": "QEvent::LeaveWhatsThisMode",
"88": "QEvent::LocaleChange",
"176": "QEvent::NonClientAreaMouseButtonDblClick",
"174": "QEvent::NonClientAreaMouseButtonPress",
"175": "QEvent::NonClientAreaMouseButtonRelease",
"173": "QEvent::NonClientAreaMouseMove",
"177": "QEvent::MacSizeChange",
"43": "QEvent::MetaCall",
"102": "QEvent::ModifiedChange",
"4": "QEvent::MouseButtonDblClick",
"2": "QEvent::MouseButtonPress",
"3": "QEvent::MouseButtonRelease",
"5": "QEvent::MouseMove",
"109": "QEvent::MouseTrackingChange",
"13": "QEvent::Move",
"197": "QEvent::NativeGesture",
"208": "QEvent::OrientationChange",
"12": "QEvent::Paint",
"39": "QEvent::PaletteChange",
"131": "QEvent::ParentAboutToChange",
"21": "QEvent::ParentChange",
"212": "QEvent::PlatformPanel",
"217": "QEvent::PlatformSurface",
"75": "QEvent::Polish",
"74": "QEvent::PolishRequest",
"123": "QEvent::QueryWhatsThis",
"106": "QEvent::ReadOnlyChange",
"199": "QEvent::RequestSoftwareInputPanel",
"14": "QEvent::Resize",
"204": "QEvent::ScrollPrepare",
"205": "QEvent::Scroll",
"117": "QEvent::Shortcut",
"51": "QEvent::ShortcutOverride",
"17": "QEvent::Show",
"26": "QEvent::ShowToParent",
"50": "QEvent::SockAct",
"192": "QEvent::StateMachineSignal",
"193": "QEvent::StateMachineWrapped",
"112": "QEvent::StatusTip",
"100": "QEvent::StyleChange",
"87": "QEvent::TabletMove",
"92": "QEvent::TabletPress",
"93": "QEvent::TabletRelease",
"171": "QEvent::TabletEnterProximity",
"172": "QEvent::TabletLeaveProximity",
"219": "QEvent::TabletTrackingChange",
"22": "QEvent::ThreadChange",
"1": "QEvent::Timer",
"120": "QEvent::ToolBarChange",
"110": "QEvent::ToolTip",
"184": "QEvent::ToolTipChange",
"194": "QEvent::TouchBegin",
"209": "QEvent::TouchCancel",
"196": "QEvent::TouchEnd",
"195": "QEvent::TouchUpdate",
"189": "QEvent::UngrabKeyboard",
"187": "QEvent::UngrabMouse",
"78": "QEvent::UpdateLater",
"77": "QEvent::UpdateRequest",
"111": "QEvent::WhatsThis",
"118": "QEvent::WhatsThisClicked",
"31": "QEvent::Wheel",
"132": "QEvent::WinEventAct",
"24": "QEvent::WindowActivate",
"103": "QEvent::WindowBlocked",
"25": "QEvent::WindowDeactivate",
"34": "QEvent::WindowIconChange",
"105": "QEvent::WindowStateChange",
"33": "QEvent::WindowTitleChange",
"104": "QEvent::WindowUnblocked",
"203": "QEvent::WinIdChange",
"126": "QEvent::ZOrderChange", }
This works for PyQt5 and PySide2. I'm just create a dict by enumerating every event type in the QEvent object. I was hoping for something more built-in though.
class EventTypes:
"""Stores a string name for each event type.
With PySide2 str() on the event type gives a nice string name,
but with PyQt5 it does not. So this method works with both systems.
"""
def __init__(self):
"""Create mapping for all known event types."""
self.string_name = {}
for name in vars(QEvent):
attribute = getattr(QEvent, name)
if type(attribute) == QEvent.Type:
self.string_name[attribute] = name
def as_string(self, event: QEvent.Type) -> str:
"""Return the string name for this event."""
try:
return self.string_name[event]
except KeyError:
return f"UnknownEvent:{event}"
# Example Usage
event_str = EventTypes().as_string(QEvent.UpdateRequest)
assert event_str == "UpdateRequest"
>>> type(QEvent.KeyPress)
<class 'PyQt5.QtCore.QEvent.Type'>
>>>
PyQt5 doesn t give name:
>>> PyQt5.QtCore.QEvent.KeyPress.name
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Type' object has no attribute 'name'
>>>
PySide2 does:
>>> (PySide2.QtCore.QEvent.KeyPress.name)
b'KeyPress'
>>>
Related
I've been trying to make grading system with this code but it has no outcome and I get empty lists. What am I doing wrong?
My code:
def grades_data():
import csv
global idlist
global namelist
global finalscorelist
for i in range(len(idlist)):
if int(finalscorelist[i])>=90 and int(finalscorelist[i])<=100:
grades.append("A")
continue
elif int(finalscorelist[i])>=75 and int(finalscorelist[i])<90:
grades.append("B")
continue
elif int(finalscorelist[i])>=60 and int(finalscorelist[i])<75:
grades.append("c")
continue
elif int(finalscorelist[i])>=50 and int(finalscorelist[i])<60:
grades.append("D")
continue
else:
grades.append("F")
print("----------------------------------------------------------------------") #to make a border
print("id\tname\tgrades")
print("----------------------------------------------------------------------") #to make a border
for (idlist,namelist,grades) in zip (idlist,namelist,grades):
print(str(idlist)+"\t"+str(namelist)+"\t\t"+str(grades))
print("----------------------------------------------------------------------") #to make a border
Output:
id name grades
----------------------------------------------------------------------
----------------------------------------------------------------------
You did not detail the layout of your csv file but I imagine it to be something like this.
id,name,score
1,"Steve",90
2,"Jill",92
3,"Billy",100
4,"Sara",75
5,"Bob",80
6,"Julie",89
7,"Wes",60
8,"Andy",74
9,"Jack",59
10,"Greta",50
11,"Wilma",48
Script
import pandas as pd
df = pd.read_csv("scores.csv")
grades = {
"A": (90, 101),
"B": (75, 90),
"C": (60, 75),
"D": (50, 60),
"F": (0, 50),
}
df["grade"] = None
for grade in grades:
idx = df[
df["score"].between(grades[grade][0], grades[grade][1], inclusive="left")
].index
df.loc[idx, "grade"] = grade
print(df)
df.to_csv("grades.csv")
You will need to pip install pandas to your working environment. This script loads the "scores.csv" file from the working directory, creates a grades dictionary, assign an empty grade column, cycles through the grades dictionary assigning the grades based on the grades dictionary and scores, and finally prints the grades dataframe, and saves a grades.csv file.
How can I evaluate if a env variable is a boolean True, in Python? Is it correct to use:
if os.environ['ENV_VAR'] is True:
.......
Option 1
I think this works well:
my_env = os.getenv("ENV_VAR", 'False').lower() in ('true', '1', 't')
It allows: things like true, True, TRUE, 1, "1", TrUe, t, T, ...
Update: After I read the commentary of Klaas, I updated the original code my_env = bool(os.getenv(... to my_env = os.getenv(... because in will result in a bool type
Option 2
UPDATE:
After the #MattG commentary, I added a new solution that raises an error for entries like ttrue instead of returning False:
# ...
import os
# ...
def get_variable(name: str, default_value: bool | None = None) -> bool:
true_ = ('true', '1', 't') # Add more entries if you want, like: `y`, `yes`, `on`, ...
false_ = ('false', '0', 'f') # Add more entries if you want, like: `n`, `no`, `off`, ...
value: str | None = os.getenv(name, None)
if value is None:
if default_value is None:
raise ValueError(f'Variable `{name}` not set!')
else:
value = str(default_value)
if value.lower() not in true_ + false_:
raise ValueError(f'Invalid value `{value}` for variable `{name}`')
return value in true_
# ...
my_env1 = get_variable("ENV_VAR1")
my_env2 = get_variable(name="ENV_VAR2") # Raise error if variable was not set
my_env3 = get_variable(name="ENV_VAR3", default_value=False) # return False if variable was not set
All the same, but thats the most readable version for me:
DEBUG = (os.getenv('DEBUG', 'False') == 'True')
Here anything but True will evaluate to False. DEBUG is False unless explicitly set to True in ENV
I recommend using strtobool function
example:
DEBUG = strtobool(os.getenv("DEBUG", "false"))
You can check them in python documentation
https://docs.python.org/3/distutils/apiref.html#distutils.util.strtobool
Only one problem, they raise an error if you pass the wrong value
Code
from distutils.util import strtobool
print("Value: ", strtobool("false"))
print("Value: ", strtobool("Wrong value"))
Output
Value: 0
Traceback (most recent call last):
File "<string>", line 9, in <module>
File "/usr/lib/python3.8/distutils/util.py", line 319, in strtobool
raise ValueError("invalid truth value %r" % (val,))
ValueError: invalid truth value 'wrong value'
Neither of the ways you have will work. os.environ['ENV_VAR'] alone will cause a KeyError if the key doesn't exist, and will return the value associated with the 'ENV_VAR' if it does. In either case, you'll error out, or compare to True or "true" which will always result in False (unless the value associated with the environment variable happens to be "true"; but that isn't what you're after).
To check if a mapping contains a particular key, you would use in:
if 'ENV_VAR' in os.environ:
# It contains the key
else:
# It doesn't contain the key
Highly recommend environs:
from environs import Env
env = Env()
MY_BOOL_VALUE = env.bool("MY_BOOL_VALUE", False)
if MY_BOOL_VALUE:
print("MY_BOOL_VALUE was set to True.")
else:
print("MY_BOOL_VALUE was either set to False or was not defined.")
Another alternative that accepts either "false", "False", "true" or "True":
import os
import ast
def getenv_bool(name: str, default: str = "False"):
raw = os.getenv(name, default).title()
return ast.literal_eval(raw)
I use the following to have more strict typing and support wider boolean variations in inputs
import os
def getenv_bool(name: str, default: bool = False) -> bool:
return os.getenv(name, str(default)).lower() in ("yes", "y", "true", "1", "t")
Usage :
feature_1=getenv_bool('FEATURE_1', False)
If you don't want to use the environs library mentioned above then strtobool is perfect for this. The only problem is it is deprecated, there does not seem to be a replacement library anywhere, but luckily it is only a few lines of simple code with no dependencies. Just implement the code:
# Copied from distutils.util.strtobool, which is deprecated
def strtobool (val):
"""Convert a string representation of truth to true (1) or false (0).
True values are case insensitive 'y', 'yes', 't', 'true', 'on', and '1'.
false values are case insensitive 'n', 'no', 'f', 'false', 'off', and '0'.
Raises ValueError if 'val' is anything else.
"""
val = val.lower()
if val in ('y', 'yes', 't', 'true', 'on', '1'):
return 1
elif val in ('n', 'no', 'f', 'false', 'off', '0'):
return 0
else:
raise ValueError("invalid truth value %r" % (val,))
Use it like this:
my_env_var_value = strtobool(os.getenv("ENV_VAR", "False"))
And YES, this will throw an error if the environment variable has some value that is neither true nor false. In the great majority of cases that is probably the best course of action.
Another possible solution is parse values as JSON values:
import json
import os
def getenv(name, default="null"):
try:
return json.loads(os.getenv(name, default))
except json.JSONDecodeError:
return name
The try is for cases when is not possible a direct conversion.
assert getenv("0") == 0
assert getenv("1.1") = 1.1
assert getenv("true") == True
assert getenv("Hello") = "Hello"
assert getenv('["list", "of", "strings"]') == ["list", "of", "strings"]
Python Version:
3.9.13 (main, May 27 2022, 17:01:00)
I used solution below and it works perfect;
In your env file;
SOMEHING_ENABLED=True
and use case in your python file;
from distutils.util import strtobool
if bool(strtobool(os.getenv('SOMEHING_ENABLED'))):
# good to go.
Not: distutils will no longer work from version 3.12
In a python project, my class has several properties that I need to be of specific type. Users of the class must have the ability to set the property.
What is the best way to do this? Two solutions come to my mind:
1. Have test routines in each setter function.
2. Use decorators for attributes
My current solution is 1 but I am not happy with it due to the code duplication. It looks like this:
class MyClass(object):
#property
def x(self):
return self._x
#x.setter
def x(self, val):
if not isinstance(self, int):
raise Exception("Value must be of type int")
self._x = val
#property
def y(self):
return self._y
#x.setter
def y(self, val):
if not isinstance(self, (tuple, set, list)):
raise Exception("Value must be of type tuple or set or list")
self._y = val
From what I know of decorators, it should be possible to have a decorator before def x(self) handle this job. Alas I fail miserably at this, as all examples I found (like this or this) are not targeted at what I want.
The first question is thus: Is it better to use a decorator to check property types? If yes, the next question is: What is wrong with below decorator (I want to be able write #accepts(int)?
def accepts(types):
"""Decorator to check types of property."""
def outer_wrapper(func):
def check_accepts(prop):
getter = prop.fget
if not isinstance(self[0], types):
msg = "Wrong type."
raise ValueError(msg)
return self
return check_accepts
return outer_wrapper
Appetizer
Callables
This is likely beyond your needs, since it sounds like you're dealing with end-user input, but I figured it may be helpful for others.
Callables include functions defined with def, built-in functions/methods such as open(), lambda expressions, callable classes, and many more. Obviously, if you only want to allow a certain type(s) of callables, you can still use isinstance() with types.FunctionType, types.BuiltinFunctionType, types.LambdaType, etc. But if this is not the case, the best solution to this that I am aware of is demonstrated by the MyDecoratedClass.z property using isinstance() with collections.abc.Callable. It's not perfect, and will return false positives in extraordinary cases (for example, if a class defines a __call__ function that doesn't actually make the class callable). The callable(obj) built-in is the only foolproof check function to my knowledge. The MyClass.z the use property demonstrates this function, but you'd have to write another/modify the existing decorator function in MyDecoratedClass in order to support the use of check functions other than isinstance().
Iterables (and Sequences and Sets)
The y property in the code you provided is supposed to be restricted to tuples, sets, and lists, so the following may be of some use to you.
Instead of checking if arguments are of individual types, you might want to consider using Iterable, Sequence, and Set from the collections.abc module. Please use caution though, as these types are far less restrictive than simply passing (tuple, set, list) as you have. abc.Iterable (as well as the others) work near-perfectly with isinstance(), although it does sometimes return false positives as well (e.g. a class defines an __iter__ function but doesn't actually return an iterator -- who hurt you?). The only foolproof method of determining whether or not an argument is iterable is by calling the iter(obj) built-in and letting it raise a TypeError if it's not iterable, which could work in your case. I don't know of any built-in alternatives to abc.Sequence and abc.Set, but almost every sequence/set object is also iterable as of Python 3, if that helps. The MyClass.y2 property implements iter() as a demonstration, however the decorator function in MyDecoratedClass does not (currently) support functions other than isinstance(); as such, MyDecoratedClass.y2 uses abc.Iterable instead.
For the completeness' sake, here is a quick comparison of their differences:
>>> from collections.abc import Iterable, Sequence, Set
>>> def test(x):
... print((isinstance(x, Iterable),
... isinstance(x, Sequence),
... isinstance(x, Set)))
...
>>> test(123) # int
False, False, False
>>> test("1, 2, 3") # str
True, True, False
>>> test([1, 2, 3]) # list
(True, True, False)
>>> test(range(3)) # range
(True, True, False)
>>> test((1, 2, 3)) # tuple
(True, True, False)
>>> test({1, 2, 3}) # set
(True, False, True)
>>> import numpy as np
>>> test(numpy.arange(3)) # numpy.ndarray
(True, False, False)
>>> test(zip([1, 2, 3],[4, 5, 6])) # zip
(True, False, False)
>>> test({1: 4, 2: 5, 3: 6}) # dict
(True, False, False)
>>> test({1: 4, 2: 5, 3: 6}.keys()) # dict_keys
(True, False, True)
>>> test({1: 4, 2: 5, 3: 6}.values()) # dict_values
(True, False, False)
>>> test({1: 4, 2: 5, 3: 6}.items()) # dict_items
(True, False, True)
Other Restrictions
Virtually all other argument type restrictions that I can think of must use hasattr(), which I'm not going to get into here.
Main Course
This is the part that actually answers your question. assert is definitely the simplest solution, but it has its limits.
class MyClass:
#property
def x(self):
return self._x
#x.setter
def x(self, val):
assert isinstance(val, int) # raises AssertionError if val is not of type 'int'
self._x = val
#property
def y(self):
return self._y
#y.setter
def y(self, val):
assert isinstance(val, (list, set, tuple)) # raises AssertionError if val is not of type 'list', 'set', or 'tuple'
self._y = val
#property
def y2(self):
return self._y2
#y2.setter
def y2(self, val):
iter(val) # raises TypeError if val is not iterable
self._y2 = val
#property
def z(self):
return self._z
#z.setter
def z(self, val):
assert callable(val) # raises AssertionError if val is not callable
self._z = val
def multi_arg_example_fn(self, a, b, c, d, e, f, g):
assert isinstance(a, int)
assert isinstance(b, int)
# let's say 'c' is unrestricted
assert isinstance(d, int)
assert isinstance(e, int)
assert isinstance(f, int)
assert isinstance(g, int)
this._a = a
this._b = b
this._c = c
this._d = d
this._e = e
this._f = f
this._g = g
return a + b * d - e // f + g
Pretty clean overall, besides the multi-argument function I threw in there at the end, demonstrating that asserts can get tedious. However, I'd argue that the biggest drawback here is the lack of Exception messages/variables. If the end-user sees an AssertionError, it has no message and is therefore mostly useless. If you write intermediate code that could except these errors, that code will have no variables/data to be able to explain to the user what went wrong. Enter the decorator function...
from collections.abc import Callable, Iterable
class MyDecoratedClass:
def isinstance_decorator(*classinfo_args, **classinfo_kwargs):
'''
Usage:
Always remember that each classinfo can be a type OR tuple of types.
If the decorated function takes, for example, two positional arguments...
* You only need to provide positional arguments up to the last positional argument that you want to restrict the type of. Take a look:
1. Restrict the type of only the first argument with '#isinstance_decorator(<classinfo_of_arg_1>)'
* Notice that a second positional argument is not required
* Although if you'd like to be explicit for clarity (in exchange for a small amount of efficiency), use '#isinstance_decorator(<classinfo_of_arg_1>, object)'
* Every object in Python must be of type 'object', so restricting the argument to type 'object' is equivalent to no restriction whatsoever
2. Restrict the types of both arguments with '#isinstance_decorator(<classinfo_of_arg_1>, <classinfo_of_arg_2>)'
3. Restrict the type of only the second argument with '#isinstance_decorator(object, <classinfo_of_arg_2>)'
* Every object in Python must be of type 'object', so restricting the argument to type 'object' is equivalent to no restriction whatsoever
Keyword arguments are simpler: #isinstance_decorator(<a_keyword> = <classinfo_of_the_kwarg>, <another_keyword> = <classinfo_of_the_other_kwarg>, ...etc)
* Remember that you only need to include the kwargs that you actually want to restrict the type of (no using 'object' as a keyword argument!)
* Using kwargs is probably more efficient than using example 3 above; I would avoid having to use 'object' as a positional argument as much as possible
Programming-Related Errors:
Raises IndexError if given more positional arguments than decorated function
Raises KeyError if given keyword argument that decorated function isn't expecting
Raises TypeError if given argument that is not of type 'type'
* Raised by 'isinstance()' when fed improper 2nd argument, like 'isinstance(foo, 123)'
* Virtually all UN-instantiated objects are of type 'type'
Examples:
example_instance = ExampleClass(*args)
# Neither 'example_instance' nor 'ExampleClass(*args)' is of type 'type', but 'ExampleClass' itself is
example_int = 100
# Neither 'example_int' nor '100' are of type 'type', but 'int' itself is
def example_fn: pass
# 'example_fn' is not of type 'type'.
print(type(example_fn).__name__) # function
print(type(isinstance).__name__) # builtin_function_or_method
# As you can see, there are also several types of callable objects
# If needed, you can retrieve most function/method/etc. types from the built-in 'types' module
Functional/Intended Errors:
Raises TypeError if a decorated function argument is not an instance of the type(s) specified by the corresponding decorator argument
'''
def isinstance_decorator_wrapper(old_fn):
def new_fn(self, *args, **kwargs):
for i in range(len(classinfo_args)):
classinfo = classinfo_args[i]
arg = args[i]
if not isinstance(arg, classinfo):
raise TypeError("%s() argument %s takes argument of type%s' but argument of type '%s' was given" %
(old_fn.__name__, i,
"s '" + "', '".join([x.__name__ for x in classinfo]) if isinstance(classinfo, tuple) else " '" + classinfo.__name__,
type(arg).__name__))
for k, classinfo in classinfo_kwargs.items():
kwarg = kwargs[k]
if not isinstance(kwarg, classinfo):
raise TypeError("%s() keyword argument '%s' takes argument of type%s' but argument of type '%s' was given" %
(old_fn.__name__, k,
"s '" + "', '".join([x.__name__ for x in classinfo]) if isinstance(classinfo, tuple) else " '" + classinfo.__name__,
type(kwarg).__name__))
return old_fn(self, *args, **kwargs)
return new_fn
return isinstance_decorator_wrapper
#property
def x(self):
return self._x
#x.setter
#isinstance_decorator(int)
def x(self, val):
self._x = val
#property
def y(self):
return self._y
#y.setter
#isinstance_decorator((list, set, tuple))
def y(self, val):
self._y = val
#property
def y2(self):
return self._y2
#y2.setter
#isinstance_decorator(Iterable)
def y2(self, val):
self._y2 = val
#property
def z(self):
return self._z
#z.setter
#isinstance_decorator(Callable)
def z(self, val):
self._z = val
#isinstance_decorator(int, int, e = int, f = int, g = int, d = (int, float, str))
def multi_arg_example_fn(self, a, b, c, d, e, f, g):
# Identical to assertions in MyClass.multi_arg_example_fn
self._a = a
self._b = b
self._c = c
self._d = d
return a + b * e - f // g
Clearly, multi_example_fn is one place where this decorator really shines. The clutter made by assertions has been reduced to a single line. Let's take a look at some example error messages:
>>> test = MyClass()
>>> dtest = MyDecoratedClass()
>>> test.x = 10
>>> dtest.x = 10
>>> print(test.x == dtest.x)
True
>>> test.x = 'Hello'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 7, in x
AssertionError
>>> dtest.x = 'Hello'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 100, in new_fn
TypeError: x() argument 0 takes argument of type 'int' but argument of type 'str' was given
>>> test.y = 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 15, in y
AssertionError
>>> test.y2 = 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 23, in y2
TypeError: 'int' object is not iterable
>>> dtest.y = 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 100, in new_fn
TypeError: y() argument 0 takes argument of types 'list', 'set', 'tuple' but argument of type 'int' was given
>>> dtest.y2 = 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 100, in new_fn
TypeError: y2() argument 0 takes argument of type 'Iterable' but argument of type 'int' was given
>>> test.z = open
>>> dtest.z = open
>>> test.z = None
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 31, in z
AssertionError
>>> dtest.z = None
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 100, in new_fn
TypeError: z() argument 0 takes argument of type 'Callable' but argument of type 'NoneType' was given
Far superior in my opinion. Everything looks good except...
>>> test.multi_arg_example_fn(9,4,[1,2],'hi', g=2,e=1,f=4)
11
>>> dtest.multi_arg_example_fn(9,4,[1,2],'hi', g=2,e=1,f=4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 102, in new_fn
KeyError: 'd'
>>> print('I forgot that you have to merge args and kwargs in order for the decorator to work properly with both but I dont have time to fix it right now. Absolutely safe for properties for the time being though!')
I forgot that you have to merge args and kwargs in order for the decorator to work properly with both but I dont have time to fix it right now. Absolutely safe for properties for the time being though!
Edit Notice: My previous answer was completely incorrect. I was suggesting the use of type hints, forgetting that they aren't actually ensured in any way. They are strictly a development/IDE tool. They still are insanely helpful though; I recommend looking into using them.
Trying to program a vending machine
I'm trying to check if the entered money is enough. (products)
But it keeps giving me that error-message. (TypeError: '>=' not supported between instances of '_Printer' and 'int')
products = {
"A": [100, "cola"],
"B": [100, "drink0"]
}
def coinCounter():
print("Bitte Münzen einwerfen! / Betrag eingeben!")
credits = int(input("Betrag: "))
def product():
print("Bitte Produkt wählen!")
choosedProduct = input("Produkt: ").capitalize()
if choosedProduct in products and credits >= products[choosedProduct][0]:
output = True
elif choosedProduct not in products:
print("Das Produkt existiernt nicht.")
elif products[choosedProduct][0] >= credits:
print("Du hast nicht genug Geld eingeworfen")
def moneyBack(moneyBack):
moneyBack = credits - products[choosedProduct][0]
print("Zurück: ", moneyBack)
def output(output, choosedProduct):
if output == True:
print("Das Produkt", choosedProduct[1], "wird ausgegeben...")
output = False
output()
moneyBack()
def main():
coinCounter()
product()
main()
The problem is one of scope.
When you set credits in coinCounter, this assignment is local to the function and not visible to the outside world. Why does your script not crash with a NameError? Because there is a global builtin object called credits, which you can call (or print) to see a short copyright notice:
>>> credits
Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
for supporting Python development. See www.python.org for more information.
You try to compare this object to an integer, which fails.
Instead, have coinCounter return the amount of credits and call coinCounter either in product, or call it in main and give the value as an argument to product:
products = {
"A": [100, "cola"],
"B": [100, "drink0"]
}
def coinCounter():
print("Bitte Münzen einwerfen! / Betrag eingeben!")
return int(input("Betrag: "))
def product(credits):
print("Bitte Produkt wählen!")
choosedProduct = input("Produkt: ").capitalize()
if choosedProduct in products and credits >= products[choosedProduct][0]:
output = True
elif choosedProduct not in products:
print("Das Produkt existiernt nicht.")
elif products[choosedProduct][0] >= credits:
print("Du hast nicht genug Geld eingeworfen")
def moneyBack(moneyBack):
moneyBack = credits - products[choosedProduct][0]
print("Zurück: ", moneyBack)
def output(output, choosedProduct):
if output == True:
print("Das Produkt", choosedProduct[1], "wird ausgegeben...")
output = False
output()
moneyBack()
def main():
credits = coinCounter()
product(credits)
main()
input() returns a string: https://docs.python.org/3.0/library/functions.html#input
"TypeError exception is raised when an operation or function is applied to an object of inappropriate type. The associated value is a string giving details about the type mismatch": https://docs.python.org/3/library/exceptions.html#TypeError
You cannot compare a string to an int, therefore you have to convert the input value to int: int(input("Betrag: ")).
`
select = input("Enter what you would like to do (enter 'x' to exit): ")
functions = {"c": calculator, "a": addsubtract, "s": squareroot,
"p": power, "n": arrange, "f": factorial} # storing break or return result in "Invalid Syntax" in the dictionary
#currently I'm doing this:
if select != "x":
functions[select]()
else:
return
I'm trying to minimize code and have been able to do so by storing functions in a dictionary; I'd like to know if there was a workaround/similar way of doing this in case it's not possible using a dictionary.
You can't:
def r():
return 1
def print_hello():
print("HelloWorld")
l = [print_hello, print_hello, print_hello, r, None]
if __name__ == '__main__':
for i in range(10):
print(i, end='')
l[i]()
$ python3 test.py
> 0HelloWorld
> 1HelloWorld
> 2HelloWorld
> 34Traceback (most recent call last):
> File "test.py", line 14, in <module>
> l[i]()
> TypeError: 'NoneType' object is not callable
And it is not a good idea to store functions in a dictionary:
https://www.python.org/dev/peps/pep-0020/
Flat is better than nested.