How to use *args in a python class? - python

I am trying to get some args working in a class, I already got it running in a function from How to use *args in a function?.
I'm trying to get that function into a class but I don't seem to understand how to initialize that class which has an init function taking *args. The code is as following :
class classname(object):
def __init__(self, *args):
<code-snip>
...
</code-snip>
if __name__ == "__main__":
start = classname()
start()
So here I'm confused on what to do with 'start()'. Do I have to use 'start(*sys.argv[1:])' or 'start()'. Both doesn't seem to work. I want to get the *args which is expected in init to be passed properly.
Any pointers please.
Thanks a ton..
======
I'm sorry if I wasn't clear on detailing how it didn't work.
a) While using start(*sys.argv[1:])
Traceback (most recent call last):
File "check.py", line 126, in <module>
start(*sys.argv[1:])
TypeError: 'check' object is not callable
b) While using start(), I get :
Traceback (most recent call last):
File "check.py", line 126, in <module>
start()
TypeError: 'check' object is not callable
These were the errors which came up.
#alko, yes you are correct. I was looking on how to get the *args in init passed properly.

Objects are instantiated by passing arguments to class constructor. They are in turn initalized with __init__ function. In your example this would be
start = ClassName(*sys.argv[1:])
that expression is processed as follows:
New instance of classname is instantiated with object.__new__(ClassName, *sys.argv[1:]) named start in local namespace. From now on start object may be referenced inside your if __name__ == "__main__" script.
Its contents are in turn initialized invoking start.__init__(*sys.argv[1:]). Note that args to __init__ are the same passed to constructor.
And read PEP 8 for python naming convention. That is:
Class names should normally use the CapWords convention.

Your example contains a class which is first instantiated - which involves calling __init__() - and then called - which is done by calling __call__().
So your stuff should be put in the call start = classname(...) in order to be passed to __init__().
The call to the newly instantiated object will fail, however, unless it contains a __call__() method. That would have been easier to answer if you had told us what
Both doesn't seem to work.
exactly means.

Related

How to run custom code in IntEnum class body

I'm trying to generate the values that will go into a custom enum instead of using literals:
from enum import IntEnum
class Test(IntEnum):
for i in range(3):
locals()['ABC'[i]] = i
del i
My desired output is three attributes, named A, B, C, with values 0, 1, 2, respectively. This is based on two expectations that I've come to take for granted about python:
The class body will run in an isolated namespace before anything else
locals during that run will refer to said isolated namespace
Once the body is done executing, I would expect the result to be not much different than calling IntEnum('Test', [('A', 0), ('B', 1), ('C', 2)]) (which works just fine BTW).
Instead, I get an error:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in Test
File "/usr/lib/python3.8/enum.py", line 95, in __setitem__
raise TypeError('Attempted to reuse key: %r' % key)
TypeError: Attempted to reuse key: 'i'
If I try doing the same with class Test: instead of class Test(IntEnum):, it works as expected. The traceback is showing the problem to be happening in enum.py. This contradicts my assumptions about how things work.
What is going on with this code, and how to I create attributes in the local namespace of the class body before IntEnum can get to them?
Background The reason that I'm trying to create the enum this way is that the "real" values are a more complex tuple, and there is a __new__ method defined to parse the tuple and assign some attributes to the individual enum objects. All that does not seem to be relevant to figuring out what is happening with the error and fixing it.
First, an explanation of what is happening. Before executing the class body, the metaclass's __prepare__ method is used to create the namespace. Normally, this is just a dict. However, enum.EnumType uses a enum._EnumDict class, which specifically prevents duplicate names from being added to the namespace. While this does not alter how the code in the class body is run, it does alter the namespace into which that code places names.
There are a couple of exceptions to the duplicate prevention, which offer potential solutions. First, the proper solution is to use the _ignore_ sunder attribute. If it gets set first, the variable i can be used normally, and will not appear in the final class:
class Test(IntEnum):
_ignore_ = ['i']
for i in range(3):
locals()['ABC'[i]] = i
Another, much hackier method is to use a dunder name, which will be ignored by the metaclass:
class Test(IntEnum):
for __i__ in range(3):
locals()['ABC'[__i__]] = __i__
del __i__
While this solution is functional, it uses dunders, which are nominally reserved by the language, and an undocumented feature, both of which are bad.

self error when launching a local command with fabric2

Here is my code :
from fabric2 import Connection
cbis = Connection.local()
with cbis.cd('/home/bussiere/Workspace/Stack/Event/'):
cbis.run('git add .')
but i have this error :
TypeError: local() missing 1 required positional argument: 'self'
how to launch a command on local with fabric2
Apparently, you should instantiate the Connection class somehow, like this:
cbis = Connection(possibly some arguments).local()
This error happens because classes are objects in Python. You could write your own example that would produce the same error:
class Test:
def function(self):
print('hey')
Test.function()
Test().function()
Here the first call does not instantiate a Test object, and thus, no implicit first argument is supplied to function. This results in an error. The second call calls the function of an instance of Test and does pass the implicit first argument (that is, that same instance) to function. This function expects exactly one argument, so everything works fine.

Decorators, calling functions without parenthesis?

I'm teaching myself how to code with the help of some online tutorials. I've encountered "decorators", I can seem to understand how it works but something bothers me. Here's the code given:
def decor(func):
def wrap():
print("-----------")
func()
print("-----------")
return wrap
def print_text():
print("Hello World")
decorated = decor(print_text)
decorated()
output:
-----------
Hello World
-----------
The things that I want to understand are:
Why do you have to call "return wrap" instead of "return wrap()"? Whereas if you don't you'll get a "TypeError: 'NoneType' object is not callable.
When I assigned the value of decorated variable. How come I also had to use "print_text" rather than "print_text()" whereas it'll raise the same TypeError if I do?
When I used the variable "decorated". Why did I have to call it like a function (adding () at the end). When I call it using "decorated" or "print(decorated)" it says something completely different?
Sorry for the dumb questions. But I'm just starting out so please bear with me. Also please make your responses beginner-friendly. Thank you
In Python, just about everything is an object. Functions are objects too. You can reference them by their name:
>>> def print_text():
... print("Hello World")
...
>>> print_text # **no** call here, this is just referencing the object
<function print_text at 0x10e3f1c80>
>>> print_text() # With a call, so now we *run* the function
Hello World
Adding () to the name told Python to call the function, which caused it to actually execute the function body, without the call, it is just showing you what the name references.
You can assign function objects to other names too. Those other names can still be called, invoking the function:
>>> other_name = print_text
>>> other_name
<function print_text at 0x10e3f1c80>
>>> other_name()
Hello World
So other_name is just another reference to the same object, and adding () (a call expression) causes the function object to be executed. print_text() and other_name() do the exact same thing, run the code inside the function.
That's what name func inside of decor() refers to; it is a reference to the same function object. You passed it in with decor(print_text). Only later on, inside wrapper() the expression func() calls that function object. If you passed in print_text() instead, you'd pass in the None object that function returned, and None can't be called:
>>> return_value = print_text()
Hello World
>>> return_value is None
True
>>> return_value()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable
Next, return wrapper returns the newly created wrapper function object to the caller. If you did return wrapper(), you'd return the result of the function, not the function object itself.
The whole point of a decorator is to replace the original function object with a new object that does extra stuff, which is why a decorator returns that replacement object (in your example wrapper) so that in future when you call decorated(), you call that wrapper function doing something extra before and after calling the original function (via the func name, which references print_text()).
So what decor(some_function) does is return a new function object, one that'll print something, call the function object that was passed in, then print something else. That new function object can then be used to replace the old function object.

Using Functions from Other .py Files

So I'm very new to Python and programming in general. I've run into an issue with my text game. I'm trying to create a function (pretty sure 'def (name) is a function, right?)in a .py called 'locationMenu' and use it in my main game file. Here is the start of my locationMenu.py (the rest is simply prints and if/elif/else statements.
from main import Engine, Hallway, Canteen, Bedroom, Room
class locationsMenu():
def locationMenu(self):
and here is the place in my main game file where I am trying to use the 'locationMenu' function that I just created.
locationMenu()
That is just one line after I made a choice with input, etc. but now I would like to skip to the locationMenu that is in the other file.
Any help much appreciated. Please don't use "big words" because as I said I'm still very new to programming in general.
locationMenu() is an instance method of the locationsMenu() class, so you'd need to create an instance and call the method on it, something like:
from locationMenu import locationsMenu
my_menu = locationsMenu() # Create a new object
my_menu.locationMenu() # Call its instance method
If you were to just try locationsMenu.locationMenu() then you'd get something like this error:
Traceback (most recent call last):
File "./prog.py", line 3, in <module>
locationsMenu.locationMenu()
TypeError: locationMenu() missing 1 required positional argument: 'self'
because locationMenu() is not a class method, and you're trying to call it in the absence of a locationsMenu object.
If you don't want it inside a class at all, then make it a regular function, and do:
locationMenu.py:
from main import Engine, Hallway, Canteen, Bedroom, Room
def locationMenu():
print("In function locationMenu()")
prog.py:
from locationMenu import locationMenu
locationMenu()

Accessing a method using getattr

When creating a new class instance, I'm trying to call a method in a different class however can't get it to work. Here's what I have:
class DataProject(object):
def __init__(self, name=none,input_file=None,datamode=None,comments=None,readnow=True):
..............
# here's where I'm trying to call the method in the other class
if type(input_file) == str:
self.input_file_format = self.input_file.split(".")[-1]
if readnow:
getattr(Analysis(),'read_'+self.input_file_format)(self,input_file)
class Analysis(object):
def __init__(self):
pass # nothing happens here atm
def read_xlsx(self,parent,input_file):
"""Method to parse xlsx files and dump them into a DataFrame"""
xl = pd.ExcelFile(input_file)
for s in sheet_names:
parent.data[s]=xl.parse(s)
I'm getting a NameError: global name 'read_xlsx' is not defined when I run this with afile.xlxs as input which made me think that I just discovered a massive hole in my Python knowledge (not that there aren't many but they tend to be hard to see, sort of like big forests...).
I would have thought that getattr(Analysis(), ... ) would access the global name space in which it would find the Analysis class and its methods. And in fact print(globals().keys()) shows that Analysis is part of this:
['plt', 'mlab', '__builtins__', '__file__', 'pylab', 'DataProject', 'matplotlib', '__package__', 'W32', 'Helpers', 'time', 'pd', 'pyplot', 'np', '__name__', 'dt', 'Analysis', '__doc__']
What am I missing here?
EDIT:
The full traceback is:
Traceback (most recent call last):
File "C:\MPython\dataAnalysis\dataAnalysis.py", line 101, in <module>
a=DataProject(input_file='C:\\MPython\\dataAnalysis\\EnergyAnalysis\\afile.xlxs',readnow=True)
File "C:\MPython\dataAnalysis\dataAnalysis.py", line 73, in __init__
getattr(Analysis(),'read_'+self.input_file_format)(self,input_file)
File "C:\MPython\dataAnalysis\dataAnalysis.py", line 90, in read_xls
read_xlsx(input_file)
NameError: global name 'read_xlsx' is not defined
My main call is:
if __name__=="__main__":
a=DataProject(input_file='C:\\MPython\\dataAnalysis\\EnergyAnalysis\\afile.xlx',readnow=True)
From the full traceback, it appears that your DataProject class is calling (successfully) the Analysys.read_xls method, which in turn is trying to call read_xlsx. However, it's calling it as a global function, not as a method.
Probably you just need to replace the code on line 90, turning read_xlsx(input_file) into self.read_xlsx(input_file), though you might need to pass an extra parameter for the parent DataProject instance too.
getattr() works as you describe it in both Python2.x and Python3.x. The bug must be somewhere else.
This modification of your code (none of the core logic is changed) works fine for instance:
class DataProject(object):
def __init__(self, name="myname",input_file="xlsx",datamode=None,comments=None,readnow=True):
if type(input_file) == str:
self.input_file_format = input_file.split(".")[-1]
if readnow:
getattr(Analysis(),'read_'+self.input_file_format)(self,input_file)
class Analysis(object):
def __init__(self):
pass # nothing happens here atm
def read_xlsx(self,parent,input_file):
"""Method to parse xlsx files and dumpt them into a DataFrame"""
print("hello")
a=DataProject()
Output is:
$ python3 testfn.py
hello
Why using getattr() in this way is usually a bad idea
The way you are using getattr forces a naming convention on your methods (read_someformat). The naming of your methods should not be a core part of your program's logic. - You should always be able to change a function's name at every call and definition of that function and leave behaviour of the program intact.
If a file format needs to be handled by a specific method this logic should be delegated to some unit (e.g a function) with responsibility for this. One way (there are others) of doing this is to have a function which takes the input and decides which function needs to handle it:
def read_file(self,file,format):
if format == `xls`:
self.read_xls(file)
if format == `csv`:
self.read_csv(file)
The above snippet does have its issues too (a better way to do it would be the chain of responsibility pattern for example) but it will be fine for small scripts and is much nicer.

Categories

Resources