Using an attribute as argument of a method of the same class - python

I want to define an attribute of a class and then use it as argument of a method in the same class in the following way
class Class1:
def __init__(self,attr):
self.attr=attr
def method1(self,x=self.attr):
return 2*x
It returns an error: NameError: name 'self' is not defined
How can I define the method in such a way that whenever I don't write x explicitly it just uses the attribute attr ?
In the example, what I mean is that I would like to have
cl=Class1()
print cl.method1(12) # returns '24'
cl.attr= -2
print cl.method1() # returns '-4'

This is because in method1, you just define the self variable in the first argument. And the self variable will only useable in the function body.
You probably think self is a special keyword. Actually self is just anormal varialbe like any variable else.
To solve the issue:
Use default value in function defination and check it in the function body:
class Class1:
def __init__(self):
self.attr = 3
def method1(self, x=None):
x = self.attr if x is None else x
return 2*x
cl = Class1()
print(cl.method1(12))
cl.attr=-2
print(cl.method1())
Result:
24
-4

In your code it seems like you are naming x as an argument you are passing to the function when in reality you are giving the init function the value, try the following code:
class Class1:
def __init__(self,attr = 3):
self.attr=attr
def method1(self):
y = (self.attr)*(2)
return y
When you call the function you should do it like this:
result = Class1(4)
print(result.method1())
>>8
P.T. Im kind of new in Python so don't give my answer for granted or as if it's the best way to solve your problem.

Related

Pytest: How to pass method which returns list as an argument in parametrized test

I have a question regarding parametrizing the test method with another method that returns the list of test data that I want to use in my test:
When I execute code in this way:
class Test:
#pytest.mark.parametrize("country_code", get_country_code_list())
def test_display_country_code(self, country_code):
print(country_code)
#classmethod
def get_country_code_list(cls) -> list:
return [1, 2, 3]
I get error: Unresolved referency: get_country_code_list. It doesn't make a difference if get_country_code_list method is a static method, class method or self.
But if I put the method get_country_code_list() above the test method, I don't get this error.
Does the order of test methods make a difference in Python?
Yes, the order in which you do things is important.
Functions are just like variables in that manner.
Working Example Variables
x = 3
y = 5
z = x + y
Works perfectly fine, because everything is done according to order.
Broken Example Variables
x = 3
z = x + y
y = 5
Doesn't work, of course, because y is neither declared nor defined when y is needed for the initialization of z.
Works just the same with functions
def bar():
return foobar()
def foo():
return bar()
foo()
def foobar()
return 5
Function foo can call function bar perfectly fine, but bar can't call foobar, because foobar is not defined yet at the execution point of foo().
This isn't a test-specific issue only.
You need to understand that #pytest.mark.parametrize is a decorator, a syntactic sugar of a class / method. When you pass an argument in a class / method, it expects the argument to be defined. Hence why this works:
def display(arg):
return print(arg)
word = "Hello World!"
display(word)
while this does not:
def display(arg):
return print(arg)
display(word)
word = "Hello World!"
Here's an example of a class-based decorator:
class MyDecorator:
def __init__(self, decor_arg):
self.decor_arg = decor_arg
def __call__(self, fn):
def wrapper(fn_arg):
return fn_arg
return self.decor_arg
def message(arg):
return f"The parameter passed is {arg}"
#MyDecorator(message)
def display(arg):
return arg
print(display("Hello World!"))
The print result:
The parameter passed is Hello World!
Given the explanation above, I'm sure you can see why the method message needs to be placed before display. If your editor has IntelliSense, changing the order of the two methods will display an error outline on #MyDecorator(concat) with the message "undefined name 'message'" or something similar.

Calling a variable from a classmethod function to a normal method

I would like to call a variable from a classmethod to a different method inside the same class:
class A():
#classmethod
def b(cls):
cls.g = 5
def c(self):
if self.g < 1:
print("TestA")
else:
print("TestB")
When doing:
x = A()
x.c()
I get:
AttributeError: 'A' object has no attribute 'g'
I've read and searched for a similar case but haven't found one. Most deal with calling variables from the init method and that doesn't apply here.
If you don't run .b() beforehand, your .g doesn't exist,...at all.
Add an __init__ function to your class and declare .g there to make sure it exists at least.
You did not define g as a class attribute of the class A. This could be done this way:
class A():
g = 7
but then in your code you are treating g as instance (self.g) and class variable (cls.g) at the same time. While this works (self.g will refer to cls.g) it may be confusing.

which position is self in python class?

In the book learning python 5th edition (o'reilly Mark Lutz)page912)
class PrivateExc(Exception): pass # More on exceptions in Part VII
class Privacy:
def __setattr__(self, attrname, value): # On self.attrname = value
if attrname in self.privates:
raise PrivateExc(attrname, self) # Make, raise user-define except
else:
self.__dict__[attrname] = value # Avoid loops by using dict key
class Test1(Privacy):
privates = ['age']
class Test2(Privacy):
privates = ['name', 'pay']
def __init__(self):
self.__dict__['name'] = 'Tom' # To do better, see Chapter 39!
Maybe it is wrong in the 5th lineraise PrivateExc(attrname, self) ,
the self argument will be set as position 1st.
Will be the line changed into raise PrivateExc(self,attrname)?Why not?
Actually it doesn't matter.
Subclassing from Exception without any additional constructor doesn't restrict what you can pass as arguments to the exception class. And you can pass them in any order you want.
The arguments passed to the PrivateExc class just get stored in the instance as the instance attribute .args
Example:
>>> class MyError(Exception):
... """MyError"""
...
>>> e = MyError("foo", "bar")
>>> e.args
('foo', 'bar')
>>> e
MyError('foo', 'bar')
What this basically means in the book you're reading is;
If you were to catch the exception PrivateExc you'd do something like this:
try:
...
except PrivateExc as error:
attrname, obj = error.args
...
When you are calling a method like this:
#!/bin/python
myinstance.some_method(a,b,c)
... then this is dispatched to some_method as: some_method(myinstance, a, b, c)
The instance through which the method was invoked is passed as your first argument. This is completely different than C++ and Java ... which use an implicit "this" reference ... a pointer valid from within your method's scope but not passed to it as an argument.
I hope that answers your question, thought the code example does nothing to clarify what you're attempting to do.
I think you are just confused about parameters in function definition and function calling.
In a class, a method(instance method) has a non-optional parameter in the first position, usually named self, in the definition, like this:
class Foo:
def foo(self, another_param):
pass
And the self references the instance that you call foo function with. If you have code like this:
f=Foo()
f.foo("test")
self references the f and another_param references the "test" string in the above code.
And then in the foo function, you can use self just like other parameters.
Suppose you have a Print function like this:
def Print(x):
print "Param:", x
Then you can make you Foo class like this:
class Foo:
def foo(self, another_param):
Print(another_param) # I think this will not confuse you
Or this:
class Foo:
def foo(self, another_param):
Print(self) # Now, you may understand this, self is just a param in function calling, like another_param
And now, change the Print function to PrivateExc(you can think it a function to create a PrivateExc instance here), you may understand it either.
Hope these examples can help you understand you question.

The self parameter with a dictionary of functions within a class

I'm attempting to create a dictionary of executable functions within a class. But having trouble getting the self parameter to work correctly.
Consider the following code:
class myclass(object):
def x(self):
return 'x'
def y(self):
return 'y'
EF= {
'a':x,
'b':y,
}
def test(self):
print self.EF['a']()
When I attempt to execute the 'test' function of the class, I get an error around the number of parameters as it evaluates and executes one of the functions in the dictionary.
>>> class myclass(object):
... def x(self):
... return 'x'
... def y(self):
... return 'y'
... EF= {
... 'a':x,
... 'b':y,
... }
... def test(self):
... print self.EF['a']()
...
>>>
>>>
>>> m=myclass()
>>> m.test()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 11, in test
TypeError: x() takes exactly 1 argument (0 given)
I've tried a few variations, including this which doesn't work.
EF= {
'a':self.x,
'b':self.y,
}
The only thing that did work was when I explicitly passed self as a parameter, like this.
... def test(self):
... print self.EF['a'](self)
...
>>> m=myclass()
>>> m.test()
x
I've seen other questions about using a dictionary to index functions, but none from within a class.
Here are my questions:
What is the proper way to do handle the self parameter?
I'd prefer to move my dictionary constant outside of the class into my constants section. Can I do that, and if so how? Should I do that?
If I should/have to have my dictionary within my class, why can't I move it to the top of the class?
That's all I got. Thanks for the help.
What is the proper way to do handle the self parameter?
Python uses the self identifier in similar ways to other imperative languages using the this identifier, but it is explicit (as explicit is better than implicit!)
This allows you to use the class as either an instantiated object, or the static class itself.
For an instantiated version, you are probably looking for
>>> class myclass:
def __init__(self):
self.EF = {'a':self.x,'b':self.y}
def x(self):
return 'x'
def y(self):
return 'y'
def test(self):
print self.EF['a']()
>>> my_test_class = myclass()
>>> my_test_class.test()
x
I'd prefer to move my dictionary constant outside of the class into my constants section. Can I do that, and if so how? Should I do that?
If you wanted to use them as static method in a dict outside your class definition, you would need to use the #staticmethod decorator
>>> class myclass(object):
#staticmethod
def x():
return 'x'
#staticmethod
def y():
return 'y'
>>> EF = {'a':myclass.x,'b':myclass.y}
>>> EF['a']()
'x'
If I should/have to have my dictionary within my class, why can't I move it to the top of the class?
Any object attributes should be defined either in the __init__ function, or by explicitly setting them.
Having the dictionary in an init method will make it work
class Myclass(object):
def x(self):
return 'x'
def y(self):
return 'y'
def __init__(self):
self.EF= {
'a':self.x,
'b':self.y
}
def test(self):
print self.EF['a']()
m=Myclass()
m.test()
In reference to your questions. The class is kind of a dictionary or named tuple of attributes and executable functions. The functions themselves only define behavior. self is a sack of state related to your instance. if you save a pointer to that function somewhere else and provide it with a given self that is an instance of your class it should work as normal.
class MyClass(object):
def __init__(self, x):
self.x = x
def fun(self):
return self.x
i = MyClass(1)
print i.fun()
f = MyClass.fun
i2 = MyClass(2)
print f(i2)
When you call using the standard i.fun() all it's doing is passing i in implicitly as the selfargument.

Type error with classmethod constructors

I'm implementing several constructors using #classobj. I'm not only setting variables, but also calling methods in the new class:
class Example:
def __init__(self):
pass
#classmethod
def constructor1(cls,x,y):
self=cls
self.__x = x
self.__somemethod(self,y)
...
I get the following error:
unbound method __somemethod() must be called with Example instance as
first argument (got classobj instance instead)
How do I resolve this problem?
If you're wanting your class method to be a constructor, you probably want to be creating an instance of the class you get passed in as cls. I suspect you're trying to do that with your self = cls line, but you're not actually creating a new instance because you've neglected to put parentheses. There are some other issues too, but I think that is the key one. Here's a fixed constructor:
#classmethod
def constructor1(cls,x,y):
self=cls() # parentheses added, to make it a call
self.__x = x
self.__somemethod(y) # self is not needed as a parameter here
return self # return the new instance
looks like __somemethod is not a classmethod, but a "normal" method.
And normal methods expect an actual instance as the first parameter, not a class.
And because constructor1 is decorated as a #classmethod, cls is the class itself - which you pass to __somemethod.
That cannot work.
You should reconsider your design approach.
Addendum:
Maybe you meant something like this?
#classmethod
def constructor1(cls, x, y):
newinst = cls()
newinst.__x = x
cls.__somemethod(newinst, y)
That'd be better written as followed, though:
#classmethod
def constructor1(cls, x, y):
newinst = cls()
newinst.__x = x
newinst.__somemethod(y)
actually, I like neighter approach - seems like a codesmell of overcomplexity to me.
This may be a template of what I think you're trying to achieve...
import random
class Something(object):
def __init__(self, value, **kwargs):
self.value = value
for k, v in kwargs.iteritems():
setattr(self, k, v)
#classmethod
def from_iterable(cls, iterable):
return cls(sum(iterable), description='came from from_iterable')
#classmethod
def make_random(cls):
return cls(random.randint(1,1000), is_random=True)
a = Something.from_iterable([1, 2, 3])
b = Something.make_random()
c = Something(56)
for obj in (a, b, c):
print type(obj), obj.value
<class '__main__.Something'> 6
<class '__main__.Something'> 308
<class '__main__.Something'> 56
Thanks to ch3ka's answer and Tim Pietzcker's comment, I found my error: I used the factory method from http://jjinux.blogspot.co.at/2008/11/python-class-methods-make-good.html and forgot the () in the line self=cls(). Now it works just fine:
class Example:
def __init__(self):
pass
#classmethod
def constructor1(cls,x,y):
self=cls()
self.__x = x
self.__somemethod(self,y)
...

Categories