Related
I am studying python, and although I think I get the whole concept and notion of Python, today I stumbled upon a piece of code that I did not fully understand:
Say I have a class that is supposed to define Circles but lacks a body:
class Circle():
pass
Since I have not defined any attributes, how can I do this:
my_circle = Circle()
my_circle.radius = 12
The weird part is that Python accepts the above statement. I don't understand why Python doesn't raise an undefined name error. I do understand that via dynamic typing I just bind variables to objects whenever I want, but shouldn't an attribute radius exist in the Circle class to allow me to do this?
EDIT: Lots of wonderful information in your answers! Thank you everyone for all those fantastic answers! It's a pity I only get to mark one as an answer.
A leading principle is that there is no such thing as a declaration. That is, you never declare "this class has a method foo" or "instances of this class have an attribute bar", let alone making a statement about the types of objects to be stored there. You simply define a method, attribute, class, etc. and it's added. As JBernardo points out, any __init__ method does the very same thing. It wouldn't make a lot of sense to arbitrarily restrict creation of new attributes to methods with the name __init__. And it's sometimes useful to store a function as __init__ which don't actually have that name (e.g. decorators), and such a restriction would break that.
Now, this isn't universally true. Builtin types omit this capability as an optimization. Via __slots__, you can also prevent this on user-defined classes. But this is merely a space optimization (no need for a dictionary for every object), not a correctness thing.
If you want a safety net, well, too bad. Python does not offer one, and you cannot reasonably add one, and most importantly, it would be shunned by Python programmers who embrace the language (read: almost all of those you want to work with). Testing and discipline, still go a long way to ensuring correctness. Don't use the liberty to make up attributes outside of __init__ if it can be avoided, and do automated testing. I very rarely have an AttributeError or a logical error due to trickery like this, and of those that happen, almost all are caught by tests.
Just to clarify some misunderstandings in the discussions here. This code:
class Foo(object):
def __init__(self, bar):
self.bar = bar
foo = Foo(5)
And this code:
class Foo(object):
pass
foo = Foo()
foo.bar = 5
is exactly equivalent. There really is no difference. It does exactly the same thing. This difference is that in the first case it's encapsulated and it's clear that the bar attribute is a normal part of Foo-type objects. In the second case it is not clear that this is so.
In the first case you can not create a Foo object that doesn't have the bar attribute (well, you probably can, but not easily), in the second case the Foo objects will not have a bar attribute unless you set it.
So although the code is programatically equivalent, it's used in different cases.
Python lets you store attributes of any name on virtually any instance (or class, for that matter). It's possible to block this either by writing the class in C, like the built-in types, or by using __slots__ which allows only certain names.
The reason it works is that most instances store their attributes in a dictionary. Yes, a regular Python dictionary like you'd define with {}. The dictionary is stored in an instance attribute called __dict__. In fact, some people say "classes are just syntactic sugar for dictionaries." That is, you can do everything you can do with a class with a dictionary; classes just make it easier.
You're used to static languages where you must define all attributes at compile time. In Python, class definitions are executed, not compiled; classes are objects just like any other; and adding attributes is as easy as adding an item to a dictionary. This is why Python is considered a dynamic language.
No, python is flexible like that, it does not enforce what attributes you can store on user-defined classes.
There is a trick however, using the __slots__ attribute on a class definition will prevent you from creating additional attributes not defined in the __slots__ sequence:
>>> class Foo(object):
... __slots__ = ()
...
>>> f = Foo()
>>> f.bar = 'spam'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute 'bar'
>>> class Foo(object):
... __slots__ = ('bar',)
...
>>> f = Foo()
>>> f.bar
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: bar
>>> f.bar = 'spam'
It creates a radius data member of my_circle.
If you had asked it for my_circle.radius it would have thrown an exception:
>>> print my_circle.radius # AttributeError
Interestingly, this does not change the class; just that one instance. So:
>>> my_circle = Circle()
>>> my_circle.radius = 5
>>> my_other_circle = Circle()
>>> print my_other_circle.radius # AttributeError
There are two types of attributes in Python - Class Data Attributes and Instance Data Attributes.
Python gives you flexibility of creating Data Attributes on the fly.
Since an instance data attribute is related to an instance, you can also do that in __init__ method or you can do it after you have created your instance..
class Demo(object):
classAttr = 30
def __init__(self):
self.inInit = 10
demo = Demo()
demo.outInit = 20
Demo.new_class_attr = 45; # You can also create class attribute here.
print demo.classAttr # Can access it
del demo.classAttr # Cannot do this.. Should delete only through class
demo.classAttr = 67 # creates an instance attribute for this instance.
del demo.classAttr # Now OK.
print Demo.classAttr
So, you see that we have created two instance attributes, one inside __init__ and one outside, after instance is created..
But a difference is that, the instance attribute created inside __init__ will be set for all the instances, while if created outside, you can have different instance attributes for different isntances..
This is unlike Java, where each Instance of a Class have same set of Instance Variables..
NOTE: - While you can access a class attribute through an instance, you cannot delete it..
Also, if you try to modify a class attribute through an instance, you actually create an instance attribute which shadows the class attribute..
How to prevent new attributes creation ?
Using class
To control the creation of new attributes, you can overwrite the __setattr__ method. It will be called every time my_obj.x = 123 is called.
See the documentation:
class A:
def __init__(self):
# Call object.__setattr__ to bypass the attribute checking
super().__setattr__('x', 123)
def __setattr__(self, name, value):
# Cannot create new attributes
if not hasattr(self, name):
raise AttributeError('Cannot set new attributes')
# Can update existing attributes
super().__setattr__(name, value)
a = A()
a.x = 123 # Allowed
a.y = 456 # raise AttributeError
Note that users can still bypass the checking if they call directly object.__setattr__(a, 'attr_name', attr_value).
Using dataclass
With dataclasses, you can forbid the creation of new attributes with frozen=True. It will also prevent existing attributes to be updated.
#dataclasses.dataclass(frozen=True)
class A:
x: int
a = A(x=123)
a.y = 123 # Raise FrozenInstanceError
a.x = 123 # Raise FrozenInstanceError
Note: dataclasses.FrozenInstanceError is a subclass of AttributeError
To add to Conchylicultor's answer, Python 3.10 added a new parameter to dataclass.
The slots parameter will create the __slots__ attribute in the class, preventing creation of new attributes outside of __init__, but allowing assignments to existing attributes.
If slots=True, assigning to an attribute that was not defined will throw an AttributeError.
Here is an example with slots and with frozen:
from dataclasses import dataclass
#dataclass
class Data:
x:float=0
y:float=0
#dataclass(frozen=True)
class DataFrozen:
x:float=0
y:float=0
#dataclass(slots=True)
class DataSlots:
x:float=0
y:float=0
p = Data(1,2)
p.x = 5 # ok
p.z = 8 # ok
p = DataFrozen(1,2)
p.x = 5 # FrozenInstanceError
p.z = 8 # FrozenInstanceError
p = DataSlots(1,2)
p.x = 5 # ok
p.z = 8 # AttributeError
As delnan said, you can obtain this behavior with the __slots__ attribute. But the fact that it is a way to save memory space and access type does not discard the fact that it is (also) a/the mean to disable dynamic attributes.
Disabling dynamic attributes is a reasonable thing to do, if only to prevent subtle bugs due to spelling mistakes. "Testing and discipline" is fine but relying on automated validation is certainly not wrong either – and not necessarily unpythonic either.
Also, since the attrs library reached version 16 in 2016 (obviously way after the original question and answers), creating a closed class with slots has never been easier.
>>> import attr
...
... #attr.s(slots=True)
... class Circle:
... radius = attr.ib()
...
... f = Circle(radius=2)
... f.color = 'red'
AttributeError: 'Circle' object has no attribute 'color'
I have a class with a str instance variable. From this instance variable, I calculate a second instance variable, which is basically just the string broken up into certain 'atoms'. The second instance variable is completely determined by the first. I've made it an instance variable because I think that it is best regarded as a 'property' of the class. I'm a bit unsure about how to treat derived instance variables. In particular:
1) I think that they should be get-only properties. However, since the computation of the derived instance variable is quite intensive, I want it to be done when the class is initiated, not when the variable is called.
2) If I make a function purely for calculating the instance variable, is there a way to mark this?
3) Also, should I pass the first instance variable as a parameter, or just read it in the method from self? (in general I'm still a bit unsure of when to pass instance variables as parameters to methods.)
4) Is there a better way to do this that I haven't mentioned?
Thanks
EDIT: Here's a simplified example of what I mean:
class Amendment:
def __init__(self, string):
self.string = string
self.atoms = generate_atoms()
def generate_atoms():
return do_something_that_takes_long(self.string)
You forgot self in a couple of places. But here's how to make .string and .atoms get-only properties. We use a couple of "private" attributes that are created during __init__, and use #property to create the actual getters.
class Amendment:
def __init__(self, string):
self._string = string
self._atoms = self.generate_atoms()
def generate_atoms(self):
#return do_something_that_takes_long(self.string)
return list(self.string)
#property
def string(self):
return self._string
#property
def atoms(self):
return self._atoms
# Test
a = Amendment('abc')
print(a.string, a.atoms)
# This will raise an error because `.string` is a get-only property.
a.string = 'xyz'
output
abc ['a', 'b', 'c']
Traceback (most recent call last):
File "./qtest.py", line 53, in <module>
a.string = 'xyz'
AttributeError: can't set attribute
If you like, you could also mark generate_atoms as private, but there's probably no need. And nothing stops an insistent user from accessing such things anyway, as the linked docs explain.
As for your 3rd question, methods should normally access the attributes they need via self. In some cases you can use the same method on different attributes, and then it makes sense to pass the attribute as a parameter, but if that's not the case it just looks weird. ;)
This article has a snippet showing usage of __bases__ to dynamically change the inheritance hierarchy of some Python code, by adding a class to an existing classes collection of classes from which it inherits. Ok, that's hard to read, code is probably clearer:
class Friendly:
def hello(self):
print 'Hello'
class Person: pass
p = Person()
Person.__bases__ = (Friendly,)
p.hello() # prints "Hello"
That is, Person doesn't inherit from Friendly at the source level, but rather this inheritance relation is added dynamically at runtime by modification of the __bases__attribute of the Person class. However, if you change Friendly and Person to be new style classes (by inheriting from object), you get the following error:
TypeError: __bases__ assignment: 'Friendly' deallocator differs from 'object'
A bit of Googling on this seems to indicate some incompatibilities between new-style and old style classes in regards to changing the inheritance hierarchy at runtime. Specifically: "New-style class objects don't support assignment to their bases attribute".
My question, is it possible to make the above Friendly/Person example work using new-style classes in Python 2.7+, possibly by use of the __mro__ attribute?
Disclaimer: I fully realise that this is obscure code. I fully realize that in real production code tricks like this tend to border on unreadable, this is purely a thought experiment, and for funzies to learn something about how Python deals with issues related to multiple inheritance.
Ok, again, this is not something you should normally do, this is for informational purposes only.
Where Python looks for a method on an instance object is determined by the __mro__ attribute of the class which defines that object (the M ethod R esolution O rder attribute). Thus, if we could modify the __mro__ of Person, we'd get the desired behaviour. Something like:
setattr(Person, '__mro__', (Person, Friendly, object))
The problem is that __mro__ is a readonly attribute, and thus setattr won't work. Maybe if you're a Python guru there's a way around that, but clearly I fall short of guru status as I cannot think of one.
A possible workaround is to simply redefine the class:
def modify_Person_to_be_friendly():
# so that we're modifying the global identifier 'Person'
global Person
# now just redefine the class using type(), specifying that the new
# class should inherit from Friendly and have all attributes from
# our old Person class
Person = type('Person', (Friendly,), dict(Person.__dict__))
def main():
modify_Person_to_be_friendly()
p = Person()
p.hello() # works!
What this doesn't do is modify any previously created Person instances to have the hello() method. For example (just modifying main()):
def main():
oldperson = Person()
ModifyPersonToBeFriendly()
p = Person()
p.hello()
# works! But:
oldperson.hello()
# does not
If the details of the type call aren't clear, then read e-satis' excellent answer on 'What is a metaclass in Python?'.
I've been struggling with this too, and was intrigued by your solution, but Python 3 takes it away from us:
AttributeError: attribute '__dict__' of 'type' objects is not writable
I actually have a legitimate need for a decorator that replaces the (single) superclass of the decorated class. It would require too lengthy a description to include here (I tried, but couldn't get it to a reasonably length and limited complexity -- it came up in the context of the use by many Python applications of an Python-based enterprise server where different applications needed slightly different variations of some of the code.)
The discussion on this page and others like it provided hints that the problem of assigning to __bases__ only occurs for classes with no superclass defined (i.e., whose only superclass is object). I was able to solve this problem (for both Python 2.7 and 3.2) by defining the classes whose superclass I needed to replace as being subclasses of a trivial class:
## T is used so that the other classes are not direct subclasses of object,
## since classes whose base is object don't allow assignment to their __bases__ attribute.
class T: pass
class A(T):
def __init__(self):
print('Creating instance of {}'.format(self.__class__.__name__))
## ordinary inheritance
class B(A): pass
## dynamically specified inheritance
class C(T): pass
A() # -> Creating instance of A
B() # -> Creating instance of B
C.__bases__ = (A,)
C() # -> Creating instance of C
## attempt at dynamically specified inheritance starting with a direct subclass
## of object doesn't work
class D: pass
D.__bases__ = (A,)
D()
## Result is:
## TypeError: __bases__ assignment: 'A' deallocator differs from 'object'
I can not vouch for the consequences, but that this code does what you want at py2.7.2.
class Friendly(object):
def hello(self):
print 'Hello'
class Person(object): pass
# we can't change the original classes, so we replace them
class newFriendly: pass
newFriendly.__dict__ = dict(Friendly.__dict__)
Friendly = newFriendly
class newPerson: pass
newPerson.__dict__ = dict(Person.__dict__)
Person = newPerson
p = Person()
Person.__bases__ = (Friendly,)
p.hello() # prints "Hello"
We know that this is possible. Cool. But we'll never use it!
Right of the bat, all the caveats of messing with class hierarchy dynamically are in effect.
But if it has to be done then, apparently, there is a hack that get's around the "deallocator differs from 'object" issue when modifying the __bases__ attribute for the new style classes.
You can define a class object
class Object(object): pass
Which derives a class from the built-in metaclass type.
That's it, now your new style classes can modify the __bases__ without any problem.
In my tests this actually worked very well as all existing (before changing the inheritance) instances of it and its derived classes felt the effect of the change including their mro getting updated.
I needed a solution for this which:
Works with both Python 2 (>= 2.7) and Python 3 (>= 3.2).
Lets the class bases be changed after dynamically importing a dependency.
Lets the class bases be changed from unit test code.
Works with types that have a custom metaclass.
Still allows unittest.mock.patch to function as expected.
Here's what I came up with:
def ensure_class_bases_begin_with(namespace, class_name, base_class):
""" Ensure the named class's bases start with the base class.
:param namespace: The namespace containing the class name.
:param class_name: The name of the class to alter.
:param base_class: The type to be the first base class for the
newly created type.
:return: ``None``.
Call this function after ensuring `base_class` is
available, before using the class named by `class_name`.
"""
existing_class = namespace[class_name]
assert isinstance(existing_class, type)
bases = list(existing_class.__bases__)
if base_class is bases[0]:
# Already bound to a type with the right bases.
return
bases.insert(0, base_class)
new_class_namespace = existing_class.__dict__.copy()
# Type creation will assign the correct ‘__dict__’ attribute.
del new_class_namespace['__dict__']
metaclass = existing_class.__metaclass__
new_class = metaclass(class_name, tuple(bases), new_class_namespace)
namespace[class_name] = new_class
Used like this within the application:
# foo.py
# Type `Bar` is not available at first, so can't inherit from it yet.
class Foo(object):
__metaclass__ = type
def __init__(self):
self.frob = "spam"
def __unicode__(self): return "Foo"
# … later …
import bar
ensure_class_bases_begin_with(
namespace=globals(),
class_name=str('Foo'), # `str` type differs on Python 2 vs. 3.
base_class=bar.Bar)
Use like this from within unit test code:
# test_foo.py
""" Unit test for `foo` module. """
import unittest
import mock
import foo
import bar
ensure_class_bases_begin_with(
namespace=foo.__dict__,
class_name=str('Foo'), # `str` type differs on Python 2 vs. 3.
base_class=bar.Bar)
class Foo_TestCase(unittest.TestCase):
""" Test cases for `Foo` class. """
def setUp(self):
patcher_unicode = mock.patch.object(
foo.Foo, '__unicode__')
patcher_unicode.start()
self.addCleanup(patcher_unicode.stop)
self.test_instance = foo.Foo()
patcher_frob = mock.patch.object(
self.test_instance, 'frob')
patcher_frob.start()
self.addCleanup(patcher_frob.stop)
def test_instantiate(self):
""" Should create an instance of `Foo`. """
instance = foo.Foo()
The above answers are good if you need to change an existing class at runtime. However, if you are just looking to create a new class that inherits by some other class, there is a much cleaner solution. I got this idea from https://stackoverflow.com/a/21060094/3533440, but I think the example below better illustrates a legitimate use case.
def make_default(Map, default_default=None):
"""Returns a class which behaves identically to the given
Map class, except it gives a default value for unknown keys."""
class DefaultMap(Map):
def __init__(self, default=default_default, **kwargs):
self._default = default
super().__init__(**kwargs)
def __missing__(self, key):
return self._default
return DefaultMap
DefaultDict = make_default(dict, default_default='wug')
d = DefaultDict(a=1, b=2)
assert d['a'] is 1
assert d['b'] is 2
assert d['c'] is 'wug'
Correct me if I'm wrong, but this strategy seems very readable to me, and I would use it in production code. This is very similar to functors in OCaml.
This method isn't technically inheriting during runtime, since __mro__ can't be changed. But what I'm doing here is using __getattr__ to be able to access any attributes or methods from a certain class. (Read comments in order of numbers placed before the comments, it makes more sense)
class Sub:
def __init__(self, f, cls):
self.f = f
self.cls = cls
# 6) this method will pass the self parameter
# (which is the original class object we passed)
# and then it will fill in the rest of the arguments
# using *args and **kwargs
def __call__(self, *args, **kwargs):
# 7) the multiple try / except statements
# are for making sure if an attribute was
# accessed instead of a function, the __call__
# method will just return the attribute
try:
return self.f(self.cls, *args, **kwargs)
except TypeError:
try:
return self.f(*args, **kwargs)
except TypeError:
return self.f
# 1) our base class
class S:
def __init__(self, func):
self.cls = func
def __getattr__(self, item):
# 5) we are wrapping the attribute we get in the Sub class
# so we can implement the __call__ method there
# to be able to pass the parameters in the correct order
return Sub(getattr(self.cls, item), self.cls)
# 2) class we want to inherit from
class L:
def run(self, s):
print("run" + s)
# 3) we create an instance of our base class
# and then pass an instance (or just the class object)
# as a parameter to this instance
s = S(L) # 4) in this case, I'm using the class object
s.run("1")
So this sort of substitution and redirection will simulate the inheritance of the class we wanted to inherit from. And it even works with attributes or methods that don't take any parameters.
I am studying python, and although I think I get the whole concept and notion of Python, today I stumbled upon a piece of code that I did not fully understand:
Say I have a class that is supposed to define Circles but lacks a body:
class Circle():
pass
Since I have not defined any attributes, how can I do this:
my_circle = Circle()
my_circle.radius = 12
The weird part is that Python accepts the above statement. I don't understand why Python doesn't raise an undefined name error. I do understand that via dynamic typing I just bind variables to objects whenever I want, but shouldn't an attribute radius exist in the Circle class to allow me to do this?
EDIT: Lots of wonderful information in your answers! Thank you everyone for all those fantastic answers! It's a pity I only get to mark one as an answer.
A leading principle is that there is no such thing as a declaration. That is, you never declare "this class has a method foo" or "instances of this class have an attribute bar", let alone making a statement about the types of objects to be stored there. You simply define a method, attribute, class, etc. and it's added. As JBernardo points out, any __init__ method does the very same thing. It wouldn't make a lot of sense to arbitrarily restrict creation of new attributes to methods with the name __init__. And it's sometimes useful to store a function as __init__ which don't actually have that name (e.g. decorators), and such a restriction would break that.
Now, this isn't universally true. Builtin types omit this capability as an optimization. Via __slots__, you can also prevent this on user-defined classes. But this is merely a space optimization (no need for a dictionary for every object), not a correctness thing.
If you want a safety net, well, too bad. Python does not offer one, and you cannot reasonably add one, and most importantly, it would be shunned by Python programmers who embrace the language (read: almost all of those you want to work with). Testing and discipline, still go a long way to ensuring correctness. Don't use the liberty to make up attributes outside of __init__ if it can be avoided, and do automated testing. I very rarely have an AttributeError or a logical error due to trickery like this, and of those that happen, almost all are caught by tests.
Just to clarify some misunderstandings in the discussions here. This code:
class Foo(object):
def __init__(self, bar):
self.bar = bar
foo = Foo(5)
And this code:
class Foo(object):
pass
foo = Foo()
foo.bar = 5
is exactly equivalent. There really is no difference. It does exactly the same thing. This difference is that in the first case it's encapsulated and it's clear that the bar attribute is a normal part of Foo-type objects. In the second case it is not clear that this is so.
In the first case you can not create a Foo object that doesn't have the bar attribute (well, you probably can, but not easily), in the second case the Foo objects will not have a bar attribute unless you set it.
So although the code is programatically equivalent, it's used in different cases.
Python lets you store attributes of any name on virtually any instance (or class, for that matter). It's possible to block this either by writing the class in C, like the built-in types, or by using __slots__ which allows only certain names.
The reason it works is that most instances store their attributes in a dictionary. Yes, a regular Python dictionary like you'd define with {}. The dictionary is stored in an instance attribute called __dict__. In fact, some people say "classes are just syntactic sugar for dictionaries." That is, you can do everything you can do with a class with a dictionary; classes just make it easier.
You're used to static languages where you must define all attributes at compile time. In Python, class definitions are executed, not compiled; classes are objects just like any other; and adding attributes is as easy as adding an item to a dictionary. This is why Python is considered a dynamic language.
No, python is flexible like that, it does not enforce what attributes you can store on user-defined classes.
There is a trick however, using the __slots__ attribute on a class definition will prevent you from creating additional attributes not defined in the __slots__ sequence:
>>> class Foo(object):
... __slots__ = ()
...
>>> f = Foo()
>>> f.bar = 'spam'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute 'bar'
>>> class Foo(object):
... __slots__ = ('bar',)
...
>>> f = Foo()
>>> f.bar
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: bar
>>> f.bar = 'spam'
It creates a radius data member of my_circle.
If you had asked it for my_circle.radius it would have thrown an exception:
>>> print my_circle.radius # AttributeError
Interestingly, this does not change the class; just that one instance. So:
>>> my_circle = Circle()
>>> my_circle.radius = 5
>>> my_other_circle = Circle()
>>> print my_other_circle.radius # AttributeError
There are two types of attributes in Python - Class Data Attributes and Instance Data Attributes.
Python gives you flexibility of creating Data Attributes on the fly.
Since an instance data attribute is related to an instance, you can also do that in __init__ method or you can do it after you have created your instance..
class Demo(object):
classAttr = 30
def __init__(self):
self.inInit = 10
demo = Demo()
demo.outInit = 20
Demo.new_class_attr = 45; # You can also create class attribute here.
print demo.classAttr # Can access it
del demo.classAttr # Cannot do this.. Should delete only through class
demo.classAttr = 67 # creates an instance attribute for this instance.
del demo.classAttr # Now OK.
print Demo.classAttr
So, you see that we have created two instance attributes, one inside __init__ and one outside, after instance is created..
But a difference is that, the instance attribute created inside __init__ will be set for all the instances, while if created outside, you can have different instance attributes for different isntances..
This is unlike Java, where each Instance of a Class have same set of Instance Variables..
NOTE: - While you can access a class attribute through an instance, you cannot delete it..
Also, if you try to modify a class attribute through an instance, you actually create an instance attribute which shadows the class attribute..
How to prevent new attributes creation ?
Using class
To control the creation of new attributes, you can overwrite the __setattr__ method. It will be called every time my_obj.x = 123 is called.
See the documentation:
class A:
def __init__(self):
# Call object.__setattr__ to bypass the attribute checking
super().__setattr__('x', 123)
def __setattr__(self, name, value):
# Cannot create new attributes
if not hasattr(self, name):
raise AttributeError('Cannot set new attributes')
# Can update existing attributes
super().__setattr__(name, value)
a = A()
a.x = 123 # Allowed
a.y = 456 # raise AttributeError
Note that users can still bypass the checking if they call directly object.__setattr__(a, 'attr_name', attr_value).
Using dataclass
With dataclasses, you can forbid the creation of new attributes with frozen=True. It will also prevent existing attributes to be updated.
#dataclasses.dataclass(frozen=True)
class A:
x: int
a = A(x=123)
a.y = 123 # Raise FrozenInstanceError
a.x = 123 # Raise FrozenInstanceError
Note: dataclasses.FrozenInstanceError is a subclass of AttributeError
To add to Conchylicultor's answer, Python 3.10 added a new parameter to dataclass.
The slots parameter will create the __slots__ attribute in the class, preventing creation of new attributes outside of __init__, but allowing assignments to existing attributes.
If slots=True, assigning to an attribute that was not defined will throw an AttributeError.
Here is an example with slots and with frozen:
from dataclasses import dataclass
#dataclass
class Data:
x:float=0
y:float=0
#dataclass(frozen=True)
class DataFrozen:
x:float=0
y:float=0
#dataclass(slots=True)
class DataSlots:
x:float=0
y:float=0
p = Data(1,2)
p.x = 5 # ok
p.z = 8 # ok
p = DataFrozen(1,2)
p.x = 5 # FrozenInstanceError
p.z = 8 # FrozenInstanceError
p = DataSlots(1,2)
p.x = 5 # ok
p.z = 8 # AttributeError
As delnan said, you can obtain this behavior with the __slots__ attribute. But the fact that it is a way to save memory space and access type does not discard the fact that it is (also) a/the mean to disable dynamic attributes.
Disabling dynamic attributes is a reasonable thing to do, if only to prevent subtle bugs due to spelling mistakes. "Testing and discipline" is fine but relying on automated validation is certainly not wrong either – and not necessarily unpythonic either.
Also, since the attrs library reached version 16 in 2016 (obviously way after the original question and answers), creating a closed class with slots has never been easier.
>>> import attr
...
... #attr.s(slots=True)
... class Circle:
... radius = attr.ib()
...
... f = Circle(radius=2)
... f.color = 'red'
AttributeError: 'Circle' object has no attribute 'color'
For example—say I want to add a helloWorld() method to Python's dict type. Can I do this?
JavaScript has a prototype object that behaves this way. Maybe it's bad design and I should subclass the dict object, but then it only works on the subclasses and I want it to work on any and all future dictionaries.
Here's how it would go down in JavaScript:
String.prototype.hello = function() {
alert("Hello, " + this + "!");
}
"Jed".hello() //alerts "Hello, Jed!"
Here's a useful link with more examples— http://www.javascriptkit.com/javatutors/proto3.shtml
You can't directly add the method to the original type. However, you can subclass the type then substitute it in the built-in/global namespace, which achieves most of the effect desired. Unfortunately, objects created by literal syntax will continue to be of the vanilla type and won't have your new methods/attributes.
Here's what it looks like
# Built-in namespace
import __builtin__
# Extended subclass
class mystr(str):
def first_last(self):
if self:
return self[0] + self[-1]
else:
return ''
# Substitute the original str with the subclass on the built-in namespace
__builtin__.str = mystr
print str(1234).first_last()
print str(0).first_last()
print str('').first_last()
print '0'.first_last()
output = """
14
00
Traceback (most recent call last):
File "strp.py", line 16, in <module>
print '0'.first_last()
AttributeError: 'str' object has no attribute 'first_last'
"""
Just tried the forbbidenfruit!
here is the code, very simple!
from forbiddenfruit import curse
def list_size(self):
return len(self)
def string_hello(self):
print("Hello, {}".format(self))
if __name__ == "__main__":
curse(list, "size", list_size)
a = [1, 2, 3]
print(a.size())
curse(str, "hello", string_hello)
"Jesse".hello()
NOTE: this QA is marked as duplicate to this one, but IMO it asks for something different. I cannot answer there, so I am answering here.
Specifically, I wanted to inherit from str and add custom attributes. Existing answers (especially the ones saying you can't) didn't quite solve it, but this worked for me:
class TaggedString(str):
"""
A ``str`` with a ``.tags`` set and ``.kwtags`` dict of tags.
Usage example::
ts = TaggedString("hello world!", "greeting", "cliche",
what_am_i="h4cker")
(ts.upper(), ts.tags, ts.kwtags)
"""
def __new__(cls, *args, **kwargs):
return super().__new__(cls, args[0])
def __init__(self, s, *tags, **kwtags):
super().__init__()
self.tags = set(tags)
self.kwtags = kwtags
Hopefully this helps someone! Cheers,
Andres
Yes indeed, but you have to define a new class of the same type and it should inherit from that type.
For example:
class list(list):
def __init__(self, *args):
super().__init__(args)
def map(self, function):
return [function(i) for i in self]
a = list(1, 2, 3, 4, 5)
def double(i):
return i * 2
print(a.map(double))
Yes, by subclassing those types. See unifying types and classes in Python.
No, this doesn't mean that actual dicts will have this type, because that would be confusing. Subclassing a builtin type is the preferred way to add functionality.
class MyString:
def __init__(self, string):
self.string = string
def bigger_string(self):
print(' '.join(self.string))
mystring = MyString("this is the string")
mystring.bigger_string()
output
t h i s i s t h e s t r i n g
Dataclass in Python 3.7
from dataclasses import dataclass
#dataclass
class St:
text : str
def bigger(self) -> None:
self.text = list(self.text)
print(" ".join(self.text))
mys = St("Hello")
mys.bigger()
output
H e l l o
Yes, we can add custom methods and attributes to built-in python types. For example, let us say, you wanna define a new method inside the list class.
Let us think of defining a 'list' class and writing your own function like as follows :
class list:
def custom_method (self):
return("Hey, I'm a custom method of list class")
#lets create an object here
obj = list([1,2,3])
print(obj.custom_method())
#The above runs fine, but a list has append() method also right?? let's try it
print(obj.append(1))
"""Now you will get Attribute error : list object has no attribute append()"""
Because, when you define class having 'list' as class name, you will no longer be able to access the 'in-built list' class methods as 'list' is treated as a user-defined class rather than a inbuilt class.
So, in order to get rid of this error, you can inherit the properties/members of 'list' class and you can define own methods or attributes. So, in this way, you can call user-defined / in-built class methods using the same class name.
Here's how it looks :
#Extending in-built list class
class list(list):
def custom_method (self):
return("Hey, I'm a custom method of list class")
obj = list([1,2,3])
print(obj.custom_method())
obj.append(1)
print(obj)
It runs fine, and outputs modified list as [1,2,3,1].
NOTE : But when you do like this, it may create some ambiguity issues in long run like naming conflicts
For example, if you had a method having same signature that of an inbuilt function in user-defined class(say 'list' here), then it will be overridden without your knowledge or notice, thus you may not be able to use its original functionality in future. Considering the above code, if you ever define a method like append(self, value), the original functionality of append() will be lost.
So, it is better to use a different class name for your class name rather than same name as inbuilt class name
For example, you can declare a class like here as follows which does not raise any errors or you will not face any naming conflicts.
class custom_list(list):
def custom_method (self):
return("Hey, I'm a custom method of list class")
obj = custom_list([1,2,3])
print(obj.custom_method())
obj.append(1)
print(obj)
Subclassing is the way to go in Python. Polyglot programmers learn to use the right tool for the right situation - within reason. Something as artfully constructed as Rails (a DSL using Ruby) is painfully difficult to implement in a language with more rigid syntax like Python. People often compare the two saying how similar they are. The comparison is somewhat unfair. Python shines in its own ways. totochto.