AttributeError | How should I call a class method? - python-3.x

My class code in edit (For presentation, not a problem for my question.)
class TxtFile:
"""Responsible for the file.
Creates objects that store the text of a '.txt' file
"""
file_count = 0
#staticmethod
def question():
"""Call question()
"""
print("""If you need:
open '.txt' file for read, use .open_file
write a '.txt' file, use .write_file
append text to '.txt' file, use .append_file""")
#classmethod
def kids(cls):
"""Counts the number of open files.
Variable keeper - file_count
"""
print('I have', cls.file_count, 'file')
def __init__(self):
self.__file_name = None
self.__file_path = None
TxtFile.file_count += 1
#property
def name(self):
if self.__file_name is None:
print('You didn\'t enter a file name.\n')
else:
return self.__file_name
#property
def path(self):
if self.__file_path is None:
print('Path not specified.')
fpath.display_catalog(DATA_PATH)
else:
return self.__file_path
#name.setter
def name(self, name):
if self.__file_name is not None:
print('The file already has the specified name.\n')
else:
if self.__file_path is not None:
fpath.search_file(self.__file_path, name)
else:
print('You did not specify the path to the',
'directory with the file.\n')
#path.setter
def path(self, path):
if self.__file_path is not None:
print('The file already has the specified path.')
else:
self.__file_path = path
def open_file(self, method='read'):
"""Responsible for opening a file.
Takes 3 arguments: read, write, append
Equal: rt, wt, at
"""
if method == 'read':
pass
if method == 'write':
pass
if method == 'append':
pass
The code I want to do:
def display_info(cls):
method_list = [method for method in dir(cls)
if callable(getattr(cls, method))
and method.startswith('__') is False]
for method in method_list:
method_doc = cls.method.__doc__
print(method + ':')
if method_doc is None:
print('This method does not have docstring.')
else:
print('This is the docstring',
method_doc)
display_info(TxtFile)
Error thrown: AttributeError: type object 'TxtFile' has no attribute 'method'
I tried in this way:
method_doc = cls.__dict__[method].__doc__
Output:
This is the docstring classmethod(function) -> method
Convert a function to be a class method.
A class method receives the class as implicit first argument,
just like an instance method receives the instance.
To declare a class method, use this idiom:
class C:
#classmethod
def f(cls, arg1, arg2, ...):
...
It can be called either on the class (e.g. C.f()) or on an instance
(e.g. C().f()). The instance is ignored except for its class.
If a class method is called for a derived class, the derived class
object is passed as the implied first argument.
Class methods are different than C++ or Java static methods.
If you want those, see the staticmethod builtin.
open_file:
This is the docstring Responsible for opening a file.
Takes 3 arguments: read, write, append
Equal: rt, wt, at
question:
This is the docstring staticmethod(function) -> method
Convert a function to be a static method.
A static method does not receive an implicit first argument.
To declare a static method, use this idiom:
class C:
#staticmethod
def f(arg1, arg2, ...):
...
It can be called either on the class (e.g. C.f()) or on an instance
(e.g. C().f()). Both the class and the instance are ignored, and
neither is passed implicitly as the first argument to the method.
Static methods in Python are similar to those found in Java or C++.
For a more advanced concept, see the classmethod builtin.
I want to have a function that will output dcstring of class methods.
I have been learning Python for less than a month, so my knowledge is limited. In the code, I use what I know. I would like to know the solution and get an answer how it works.

Related

OOP - Python - printing instance variable too when I call static method alone

Here in this code I am just calling out my static method, but it prints my instance variable too. Could you please explain the reason for that, and how to avoid them being printed?
Like below:
I am a static Method
None
class Player:
def __init__(self, name = None):
self.name = name # creating instance variables
#staticmethod
def demo():
print("I am a static Method")
p1 = Player()
print(p1.demo())
As Python docs says:
Print objects to the text stream file, separated by sep and followed
by end. sep, end, file, and flush, if present, must be given as
keyword arguments.
So you can return your message in method and then just print it:
class Player:
def __init__(self, name = None):
self.name = name # creating instance variables
#staticmethod
def demo():
return "I am a static Method"
p1 = Player()
print(p1.demo())

How to call a function within a class in Python3

When running this code, I am attempting to get the same output in flipper.swim() that I get from Salmon.swim(). The Salmon.swim() properly outputs the string from that method, but when I call flipper.swim(), I get the following error: TypeError: swim() takes 0 positional arguments but 1 was given
class Fish (object):
def __init__(self, name):
self.name = name
def swim():
print("The fish swam.")
class Salmon(Fish):
pass
flipper = Fish("FLIPPER")
Salmon.swim()
Fish.swim()
print(flipper.name)
flipper.swim()
I think this is what you are trying to do
class Fish (object):
def __init__(self, name):
self.name = name
def swim(self):
print(self.name + " swam.")
class Salmon(Fish):
pass
flipper = Fish("FLIPPER")
salmon = Salmon("FOO")
Salmon.swim(flipper)
Fish.swim(flipper)
print(flipper.name)
Fish.swim(flipper)
Fish.swim(salmon)
The above will output:
FLIPPER swam.
FLIPPER swam.
FLIPPER
FLIPPER swam.
FOO swam.
you get the error because python automatically pass the instance of your class (flipper in your sample code) as the first argument to your method swim, that by convention we call self, to turn off this behavior you need to specify that said method doesn't require/use your instance of this class, and that is done with the staticmethod decorator
class Fish (object):
def __init__(self, name):
self.name = name
#staticmethod
def swim():
print("The fish swam.")

python3 implementing a data descriptor in metaclass

What is the correct way to implement a data descriptor inside a metaclass? In the following (trivial) example, I wish to always append a question mark to the desired value before setting it:
class AddQDesc:
def __init__ (self, name):
self.name = name
def __get__ (self, instance, owner=None):
obj = instance if instance != None else owner
return obj.__dict__[self.name]
def __set__ (self, instance, value):
# What should go here ?
#setattr(instance, self.name, "{}?".format(value)) <- This gives me recursion error
#instance.__dict__[self.name] = "{}?".format(value) <- This gives me proxymapping error
pass
class Meta (type):
var = AddQDesc("var")
class C (metaclass=Meta):
var = 5
print(C.var)
C.var = 1
print(C.var)
First, it looks like the descriptor was not used when I initialized var to 5. Can I somehow apply descriptor protocol here as well? (Make it "5?")
Second, how should the value be updated in the __set__ method? Updating the __dict__ gives me "TypeError: 'mappingproxy' object does not support item assignment" and using setattr gives me "RecursionError: maximum recursion depth exceeded while calling a Python object".
As I commented in the question, this is tricky - because there is no way from Python code to change a class' __dict__ attribute directly - one have to call setattr and let Python set a class attribute - and, setattr will "see" the descriptor in the metaclass, and call its __set__ instead of modifying the value in the class __dict__ itself. So, you get an infinite recursion loop.
Therefore, if you really require that the attribute proxied by the descriptor will "live" with the same name in the class'dict, you have to resort to: when setting the value, temporarily remove the descriptor from the metaclass, call setattr to set the value, and then restoring it back.
Also, if you want the values set in the class body to be handled
through the descriptor, they have to be set with setattr after the
class is created - type.__new__ won't check for the descriptor
as it builds the initial class __dict__.
from threading import Lock
class AddQDesc:
def __init__ (self, name):
self.name = name
self.lock = Lock()
def __get__ (self, instance, owner=None):
obj = instance if instance != None else owner
return obj.__dict__[self.name]
def __set__ (self, instance, value):
owner_metaclass = type(instance)
with self.lock:
# Temporarily remove the descriptor to avoid recursion problems
try:
# Note that a metaclass-inheritance hierarchy, where
# the descriptor might be placed in a superclass
# of the instance's metaclass, is not handled here.
delattr(owner_metaclass, self.name)
setattr(instance, self.name, value + 1)
finally:
setattr(owner_metaclass, self.name, self)
class Meta (type):
def __new__(mcls, name, bases, namespace):
post_init = {}
for key, value in list(namespace.items()):
if isinstance(getattr(mcls, key, None), AddQDesc):
post_init[key] = value
del namespace[key]
cls = super().__new__(mcls, name, bases, namespace)
for key, value in post_init.items():
setattr(cls, key, value)
return cls
var = AddQDesc("var")
class C (metaclass=Meta):
var = 5
print(C.var)
C.var = 1
print(C.var)
If you don't need the value to live in the class' __dict__, I'd suggest just storing it elsewhere - a dictionary in the descriptor instance for example, having the classes as keys, will suffice - and will be far less weird.
class AddQDesc:
def __init__ (self, name):
self.name = name
self.storage = {}
def __get__ (self, instance, owner):
if not instance: return self
return self.storage[instance]
def __set__ (self, instance, value):
self.storage[instance] = value + 1

Decorators unexpectedly change constructor behavior in Python

Below, I show a simplified example of a more complicated code, but nonetheless, it fully represents the issue that I have encountered.
Part 1: this works fine, no issues:
class Animal():
def __init__(self, animal_name = "no name given"):
self.set_name(animal_name)
def get_name(self):
return self._animal_name
def set_name(self, animal_name):
self._animal_name = animal_name
class Dog(Animal):
def __init__(self, dog_breed = "no breed", dog_name = "no name given"):
self._dog_breed = dog_breed
super().__init__(dog_name)
def get_breed(self):
print(self._dog_breed)
x = Dog('Greyhound', 'Rich')
Part 2: after introducing getter & setter decorators, the code stops working:
class Animal():
def __init__(self, animal_name = "no name given"):
#THE LINE BELOW SEEMS TO CAUSE AN ISSUE
self.name(animal_name)
#property
def name(self):
return self._animal_name
#name.setter
def name(self, animal_name):
self._animal_name = animal_name
class Dog(Animal):
def __init__(self, dog_breed = "no breed", dog_name = "no name given"):
self._dog_breed = dog_breed
super().__init__(dog_name)
def get_breed(self):
print(self._dog_breed)
x = Dog('Greyhound', 'Rich')
Output: AttributeError: 'Dog' object has no attribute '_animal_name'
When I keep the decorators in Part 2 but change the constructor in the Animal class to:
class Animal():
def __init__(self, animal_name = "no name given"):
self._animal_name=animal_name
It works.
I am just curious why it doesn't work in the example above in Part 2?
Short answer:
The line
self.name(animal_name)
can be split in two parts:
tmp = self.name
tmp(animal_name)
First, self.name calls the getter and the result is treated as a function. The getter uses return self._animal_name and since the setter has never been called, the respective error occurs.
Long answer:
Let's take the following class:
class Animal:
def __init__(self, animal_name):
self.name(animal_name)
#property
def name(self):
return self._animal_name
#name.setter
def name(self, animal_name):
self._animal_name = animal_name
To understand what the line
self.name(animal_name)
actually does, you first need to understand decorators.
The code
#dec
def func(a, b, ...):
[...]
is equivalent to
def func_impl(a, b, ...):
[...]
func = dec(func_impl)
(except that you can not call func_impl directly). See, for example, PEP 318 for more information.
This means that you can write the Animal class from above without using decorators:
class Animal:
def __init__(self, animal_name):
self.name(animal_name)
def get_name(self):
return self._animal_name
name = property(get_name)
def set_name(self, animal_name):
self._animal_name = animal_name
name = name.setter(set_name)
In order to understand this code, you need to understand the builtin property, which is a class. See the python docs for detailed information.
The line name = property(get_name) creates an object of type property. When retrieving the value of the property, get_name is called.
The line name = name.setter(set_name) first calls name.setter(set_name), which creates a copy of the property, and then overwrites name with this copy. When assigning a value to the copy, set_name is called.
All in all, name is an object of type property that uses get_name as getter and set_name as setter.
How does this help?
You need to understand this: name is not a function. It is a property. It is not callable.
The problematic line
self.name(animal_name)
is actually equivalent to
self.get_name()(animal_name)
which this explains the error message: The constructor calls the getter, which tries to use return self._animal_name. But since the setter has not been called, yet, self._animal_name has not been set.

class instance from nowhere [duplicate]

If I have a class ...
class MyClass:
def method(arg):
print(arg)
... which I use to create an object ...
my_object = MyClass()
... on which I call method("foo") like so ...
>>> my_object.method("foo")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: method() takes exactly 1 positional argument (2 given)
... why does Python tell me I gave it two arguments, when I only gave one?
In Python, this:
my_object.method("foo")
... is syntactic sugar, which the interpreter translates behind the scenes into:
MyClass.method(my_object, "foo")
... which, as you can see, does indeed have two arguments - it's just that the first one is implicit, from the point of view of the caller.
This is because most methods do some work with the object they're called on, so there needs to be some way for that object to be referred to inside the method. By convention, this first argument is called self inside the method definition:
class MyNewClass:
def method(self, arg):
print(self)
print(arg)
If you call method("foo") on an instance of MyNewClass, it works as expected:
>>> my_new_object = MyNewClass()
>>> my_new_object.method("foo")
<__main__.MyNewClass object at 0x29045d0>
foo
Occasionally (but not often), you really don't care about the object that your method is bound to, and in that circumstance, you can decorate the method with the builtin staticmethod() function to say so:
class MyOtherClass:
#staticmethod
def method(arg):
print(arg)
... in which case you don't need to add a self argument to the method definition, and it still works:
>>> my_other_object = MyOtherClass()
>>> my_other_object.method("foo")
foo
In simple words
In Python you should add self as the first parameter to all defined methods in classes:
class MyClass:
def method(self, arg):
print(arg)
Then you can use your method according to your intuition:
>>> my_object = MyClass()
>>> my_object.method("foo")
foo
For a better understanding, you can also read the answers to this question: What is the purpose of self?
Something else to consider when this type of error is encountered:
I was running into this error message and found this post helpful. Turns out in my case I had overridden an __init__() where there was object inheritance.
The inherited example is rather long, so I'll skip to a more simple example that doesn't use inheritance:
class MyBadInitClass:
def ___init__(self, name):
self.name = name
def name_foo(self, arg):
print(self)
print(arg)
print("My name is", self.name)
class MyNewClass:
def new_foo(self, arg):
print(self)
print(arg)
my_new_object = MyNewClass()
my_new_object.new_foo("NewFoo")
my_bad_init_object = MyBadInitClass(name="Test Name")
my_bad_init_object.name_foo("name foo")
Result is:
<__main__.MyNewClass object at 0x033C48D0>
NewFoo
Traceback (most recent call last):
File "C:/Users/Orange/PycharmProjects/Chapter9/bad_init_example.py", line 41, in <module>
my_bad_init_object = MyBadInitClass(name="Test Name")
TypeError: object() takes no parameters
PyCharm didn't catch this typo. Nor did Notepad++ (other editors/IDE's might).
Granted, this is a "takes no parameters" TypeError, it isn't much different than "got two" when expecting one, in terms of object initialization in Python.
Addressing the topic: An overloading initializer will be used if syntactically correct, but if not it will be ignored and the built-in used instead. The object won't expect/handle this and the error is thrown.
In the case of the sytax error: The fix is simple, just edit the custom init statement:
def __init__(self, name):
self.name = name
Newcomer to Python, I had this issue when I was using the Python's ** feature in a wrong way. Trying to call this definition from somewhere:
def create_properties_frame(self, parent, **kwargs):
using a call without a double star was causing the problem:
self.create_properties_frame(frame, kw_gsp)
TypeError: create_properties_frame() takes 2 positional arguments but 3 were given
The solution is to add ** to the argument:
self.create_properties_frame(frame, **kw_gsp)
As mentioned in other answers - when you use an instance method you need to pass self as the first argument - this is the source of the error.
With addition to that,it is important to understand that only instance methods take self as the first argument in order to refer to the instance.
In case the method is Static you don't pass self, but a cls argument instead (or class_).
Please see an example below.
class City:
country = "USA" # This is a class level attribute which will be shared across all instances (and not created PER instance)
def __init__(self, name, location, population):
self.name = name
self.location = location
self.population = population
# This is an instance method which takes self as the first argument to refer to the instance
def print_population(self, some_nice_sentence_prefix):
print(some_nice_sentence_prefix +" In " +self.name + " lives " +self.population + " people!")
# This is a static (class) method which is marked with the #classmethod attribute
# All class methods must take a class argument as first param. The convention is to name is "cls" but class_ is also ok
#classmethod
def change_country(cls, new_country):
cls.country = new_country
Some tests just to make things more clear:
# Populate objects
city1 = City("New York", "East", "18,804,000")
city2 = City("Los Angeles", "West", "10,118,800")
#1) Use the instance method: No need to pass "self" - it is passed as the city1 instance
city1.print_population("Did You Know?") # Prints: Did You Know? In New York lives 18,804,000 people!
#2.A) Use the static method in the object
city2.change_country("Canada")
#2.B) Will be reflected in all objects
print("city1.country=",city1.country) # Prints Canada
print("city2.country=",city2.country) # Prints Canada
It occurs when you don't specify the no of parameters the __init__() or any other method looking for.
For example:
class Dog:
def __init__(self):
print("IN INIT METHOD")
def __unicode__(self,):
print("IN UNICODE METHOD")
def __str__(self):
print("IN STR METHOD")
obj = Dog("JIMMY", 1, 2, 3, "WOOF")
When you run the above programme, it gives you an error like that:
TypeError: __init__() takes 1 positional argument but 6 were given
How we can get rid of this thing?
Just pass the parameters, what __init__() method looking for
class Dog:
def __init__(self, dogname, dob_d, dob_m, dob_y, dogSpeakText):
self.name_of_dog = dogname
self.date_of_birth = dob_d
self.month_of_birth = dob_m
self.year_of_birth = dob_y
self.sound_it_make = dogSpeakText
def __unicode__(self, ):
print("IN UNICODE METHOD")
def __str__(self):
print("IN STR METHOD")
obj = Dog("JIMMY", 1, 2, 3, "WOOF")
print(id(obj))
If you want to call method without creating object, you can change method to static method.
class MyClass:
#staticmethod
def method(arg):
print(arg)
MyClass.method("i am a static method")
I get this error when I'm sleep-deprived, and create a class using def instead of class:
def MyClass():
def __init__(self, x):
self.x = x
a = MyClass(3)
-> TypeError: MyClass() takes 0 positional arguments but 1 was given
You should actually create a class:
class accum:
def __init__(self):
self.acc = 0
def accumulator(self, var2add, end):
if not end:
self.acc+=var2add
return self.acc
In my case, I forgot to add the ()
I was calling the method like this
obj = className.myMethod
But it should be is like this
obj = className.myMethod()

Resources