Attribute Error: object has no attribute

Consider the main cases of Attribute Error: object has no attribute.

Table of Contents

There is no such attribute

If the Python object does not have an attribute with the requested name, we will receive an AttributeError.

- The object may not have an attribute with that name.
- We made a typo or misspelled the attribute.
- The attribute has not yet been created.

We can check the list of existing attributes using dir(object).

obj = "Python"

# list of atributes
print(dir(obj))

Expecting for another object

We may mistakenly expect an object of a different type as a result of some operations.

For example, a package has different functions or class methods that return data in various forms.

import package

result = package.foo()

# we are trying to use the list method on a generator object
result.append(4)
# AttributeError: 'generator' object has no attribute 'append'

In this particular case, we can first convert the result to a list.


A similar error may occur when using method chaining. Using method chaining in tkinter, tk.Entry().pack(), results in a non-obvious error. The pack, grid, and place return None.

import tkinter as tk

root = tk.Tk()

field = tk.Entry() # Entry object
field.pack()

field1 = tk.Entry().pack() # return None

print(field, type(field))
# .!entry <class 'tkinter.Entry'>

print(field1, type(field1))
# None <class 'NoneType'>

field1.get()
# AttributeError: 'NoneType' object has no attribute 'get'

Some functions are designed to return different results depending on the circumstances. For example, dict.get() returns None if the key is not found in the dictionary.

d = {
    "id1": {"a": "A"}
}

result = d.get("id1")
print(result.keys())
# dict_keys(['a'])

result = d.get("id2") # there is no such key
print(result.keys())
# AttributeError: 'NoneType' object has no attribute 'keys'

API changes

While using some Python packages, we may encounter AttributeError due to API changes. Every major version update contains breaking changes. Some functions or classes may be deprecated and removed in a new version. Or vice versa, we are trying to use new functionality that is not available in older versions. Make sure you have the appropriate version of the package installed.

In this case, we may get an error for the module.

import modulename

# attempting to access an attribute
modulename.attrname

# AttributeError: module 'modulename' has no attribute 'attrname'

Standard modules may also differ in different versions of Python.


Reassigned names and Name collision.

Python allows you to reuse names, including the names of built-in functions and PSL modules. The same things can happen when we use PyPI packages.

import keyword

# reassigning the name "keyword" to a string object
keyword = "Python"

# attempting to get the module attribute
print(keyword.kwlist)
# AttributeError: 'str' object has no attribute 'kwlist'

If we define some variables, functions, or classes in our code, we need to make sure that their names are unique in the current namespace.

Name collisions can occur when using wildcard import. For example, if the imported modules have classes with the same names but different attributes. Depending on the order in which the modules are imported, we will get different objects.

from tkinter import *
from tkinter.ttk import *

print(Button)
# <class 'tkinter.ttk.Button'>
from tkinter.ttk import *
from tkinter import *

print(Button)
# <class 'tkinter.Button'>

A variation of the naming confusion can occur when we name files with our code the same as named standard library modules or third-party packages.


Wrong object

We are using the wrong object to access the attribute. For example, we are trying to access an instance attribute through a class.

class A:
    def __init__(self, value):
        self.value = value

a = A(12)

print(a.value)
# 12

print(A.value)
# AttributeError: type object 'A' has no attribute 'value'

Typo in __init__

Due to the incorrect spelling, _init_ is not a special method but a regular class method. It is not called when an instance is created, so self attributes are not created either.

class A:
    def _init_(self):
        self.data = []
         
    def method(self, num):
        self.data.append(num)

a = A()

a.method(1)
# AttributeError: 'A' object has no attribute 'data'

print(a.data)
# AttributeError: 'A' object has no attribute 'data'

Deleted or changed attribute

User-defined functions, classes, and class instances allow you to add, modify, and remove arbitrary attributes on the fly.

class A:
    attr = 1
     
print(A.attr)
# 1

del A.attr

print(A.attr)
# AttributeError: type object 'A' has no attribute 'attr'

Attributes of a built-in type

Manipulation of attributes of Python built-in types is limited. Setting an attribute on an instance of a built-in type will result in an error.

obj = [1, 2]

obj.attr = 12
# AttributeError: 'list' object has no attribute 'attr'

print.attr = 1
# AttributeError: 'builtin_function_or_method' object has no attribute 'attr'

Access to attributes is restricted

Classes can restrict access to their attributes. We can define public and "private" attributes of an object.

There is a convention that a name prefixed with an underscore (a variable or function name) should be considered a non-public part of the API. Names with two leading underscores are automatically renamed by prefixing their name with the class name and one leading underscore. This is known as name mangling.

class A:
    __private1 = 1

    def __init__(self):
        self.__private2 = 2

a = A()

print(dir(A))
# ['_A__private1', ...]
print(dir(a))
# ['_A__private1', '_A__private2', ...]

print(a.__private1)
# AttributeError: 'A' object has no attribute '__private1'. Did you mean: '_A__private1'?
print(a.__private2)
# AttributeError: 'A' object has no attribute '__private2'. Did you mean: '_A__private2'?
print(A.__private1)
# AttributeError: type object 'A' has no attribute '__private1'. Did you mean: '_A__private1'?

If a class defines __slots__, then it explicitly declares the allowed data attributes for instances.

class A:
    __slots__ = ("value",)

    def __init__(self, value):
        self.value = value

a = A(1)

print(a.value)
# 1

a.value2 = 2
# AttributeError: 'A' object has no attribute 'value2'

Python also has options for customizing attribute lookups: __get__, __set__, __delete__, __getattr__, __getattribute__, __setattr__, __delattr__, and @property.


References:

  1. Docs: AttributeError
  2. Glossary: attribute
  3. Docs: "Private" Variables
  4. Docs: Private name mangling
  5. Docs: object.__slots__
  6. Glossary: __slots__
  7. Docs: Customizing attribute access
  8. Docs: Python 2, Attribute Access

Popular posts from this blog