Do Python's slots contain methods? - python

Let's say I do this class:
class Person:
__slots__ = ["j"]
def __init__(self):
self.j = 1
def hello(self):
print("Hello")
Is the method hello in the slots?

Whether or not you're using __slots__ to control instance attributes, methods are stored on the class, not the instance:
>>> class Slots:
__slots__ = ['attr']
def __init__(self):
self.attr = None
def method(self):
pass
>>> class NoSlots:
def __init__(self):
self.attr = None
def method(self):
pass
>>> 'method' in Slots.__dict__
True
>>> 'method' in NoSlots.__dict__
True
>>> 'method' in NoSlots().__dict__
False
Using __slots__ actually makes all defined attributes descriptors (see also the how-to), which are also stored on the class:
>>> 'attr' in Slots.__dict__
True
>>> type(Slots.attr)
<class 'member_descriptor'>

Related

A method to make a Python object change its own class?

I'm not even sure if this is possible. What I'm trying to do is having an object call a method, which would reassign a new class object to the variable
Definition:
class A():
def __init__(self):
self.number = 1
def becomeB(self):
# a method that will assign this variable a class B object
class B():
def __init__(self):
self.name = "I am B"
What I'm trying to achieve:
myObj = A()
myObj.becomeB()
print(myObj.name)
Output: I am B
It doesn't make any sense to convert object of type A to be of type B.
Instead, I would recommend you to make becomeB a function that returns object B.
For example:
class B():
def __init__(self):
self.name = "I am B"
class A():
def __init__(self):
self.number = 1
def becomeB(self):
return B()
myObj = A()
b = myObj.becomeB()
print(b.name) # output: I am B
If you want an object to behave like an instance from another class you can reassign the __class__ attribute:
def becomeB(self):
self.__class__ = B
Here's a more complete example:
class A:
def __init__(self, name):
self.name = name
def foo(self):
return f'A says: Hello {self.name}'
def becomeB(self):
self.__class__ = B
class B:
def foo(self):
return f'B says: Hello {self.name}'
a = A('World')
print(a.foo()) # prints: A says: Hello World
a.becomeB()
print(a.foo()) # prints: B says: Hello World

There are class methods. Can I have class attributes?

Consider the code snippet for the base class Base and its two child classes ChildA and ChildB:
class Base():
map = {}
def __init__(self, x):
type(self).map[x] = self
#classmethod
def get_state(cls, x):
if x in cls.map:
return cls.map[x]
return None
class ChildA(Base):
pass
class ChildB(Base):
pass
When creating an instance a = ChildA('foo') and calling ChildB.get_state('foo') after this, it returns the instance of ChildA.
However, expected (or rather the wishful behaviour) was None. The map attribute seems to be attached to the parent class Base, and the child classes share it.
Of course I could simply add a specific attribute to the child classes
class ChildA(Base):
map = {}
class ChildB(Base):
map = {}
but this is verbose if you have many child classes and several of those attributes. Is there any way to solve this by inheritance from Base?
You can use the init subclass hook:
class Base:
def __init_subclass__(cls, **kwargs):
cls.map = {}
Demo:
>>> class ChildA(Base):
... ...
...
>>> class ChildB(Base):
... ...
...
>>> ChildA.map == ChildB.map == {}
True
>>> ChildA.map is ChildB.map
False

Python decorated class documentation

I'm using a decorator for singletons in python, implemented as shown below.
I'd like to have the pydoc of the decorated class being exactly the same as the pydoc of the non decorated class, but I don't find how:
Without the getSingletonInstance.__doc__ = cls.__doc__ line, the pydoc of the decorated class gives the pydoc of the singleton function.
With the getSingletonInstance.__doc__ = cls.__doc__ line, the pydoc of the decorated class gives only the "top level" docstring.
How can I proceed?
Thanks.
def singleton(cls):
"""A singleton decorator
Warnings
--------
Singleton gdecorated calsses cannot be inhehited
Example
-------
>>> from decorators import singleton
>>> #singleton
... class SingletonDemo():
... pass
>>> d1 = SingletonDemo()
>>> d1.a = 0xCAFE
>>> d2 = SingletonDemo()
>>> id(d1) == id(d2)
True
>>> d1 == d2
True
>>> d2.a == 0xCAFE
True
References
----------
See case 2 of https://www.python.org/dev/peps/pep-0318/#examples
"""
_instances = {}
def getSingletonInstance():
if cls not in _instances:
_instances[cls] = cls()
return _instances[cls]
getSingletonInstance.__doc__ = cls.__doc__
return getSingletonInstance
Try using functools.wraps:
import functools
def wrapper(cls):
_instances = {}
class inner(cls):
def __new__(subcls):
if subcls not in _instances:
_instances[subcls] = object.__new__(subcls)
return _instances[subcls]
inner.__doc__=cls.__doc__
return inner
#wrapper
class A(object):
"""Example Docstring"""
def method(self):
"""Method Docstring"
A.__doc__
# "Example Docstring"
A.method.__doc__
# "Method Docstring"
Answer summarizing all the discussions, with example.
Thanks a lot to everybody.
This solution:
Preserves the docstring
Preserves the type
Supports static and class methods
Limitations:
Singletons cannot be inherited, which is meaningful regarding to the context
Solution:
def singleton(cls):
"""A singleton decorator
Warnings
--------
Singleton decorated classes cannot be inhehited
Example
-------
>>> import abc
>>>
>>> #singleton
... class A():
... "Ad-hoc documentation of class A"
... def __init__(self):
... "Ad-hoc documentation of class A constructor"
... print("constructor called")
... self.x = None
... #classmethod
... def cm(cls):
... "a class method"
... print("class method called")
... def im(self):
... "an instance method"
... print("instance method called")
... #staticmethod
... def sm():
... "a static method"
... print("static method called")
...
>>> #singleton
... class P(abc.ABCMeta):
... #abc.abstractmethod
... def __init__(self):
... pass
...
>>> class C(P):
... def __init__(self):
... print("C1 constructor called")
...
>>> a1 = A()
constructor called
>>> a1.x = 0xCAFE
>>> a1.x
51966
>>> a2 = A()
>>> a2.x
51966
>>> a1.x == a2.x
True
>>> a1 == a2
True
>>> id(a1) == id(a2)
True
>>> type(a1) == type(a2)
True
>>> isinstance(a1, A)
True
>>> ta1 = type(a1)
>>> issubclass(ta1, A)
True
>>> A.cm()
class method called
>>> a1.cm()
class method called
>>> A.sm()
static method called
>>> a1.sm()
static method called
>>> a1.im()
instance method called
>>> try:
... C()
... except Exception as e:
... type(e)
...
<class 'TypeError'>
"""
_instances = {}
_constructorCalled = []
class inner(cls):
def __new__(subcls):
if subcls not in _instances:
_instances[subcls] = cls.__new__(subcls)
return _instances[subcls]
def __init__(self):
if type(self) not in _constructorCalled:
cls.__init__(self)
_constructorCalled.append(type(self))
__init__.__doc__ = cls.__init__.__doc__
__new__.__doc__ = cls.__new__.__doc__
if __new__.__doc__ == (
"Create and return a new object. "
"See help(type) for accurate signature."
):
__new__.__doc__ = "Returns a singleton instance"
inner.__doc__ = cls.__doc__
return inner

How to change parent attribute in subclass python

I have the following class
class Foo():
data = "abc"
And i subclass it
class Bar(Foo):
data +="def"
I am trying to edit a parent class attribute in subclass. I want my parent class to have some string, and my subclass should add some extra data to that string. How it should be done in Python? Am i wrong by design?
You ask two questions:
How it should be done in Python?
class Bar(Foo):
data = Foo.data + "def"
Am i wrong by design?
I generally don't use class variables in Python. A more typical paradigm is to initialize an instance variable:
>>> class Foo(object):
... data = "abc"
...
>>> class Bar(Foo):
... def __init__(self):
... super(Bar, self).__init__()
... self.data += "def"
...
>>> b = Bar()
>>> b.data
'abcdef'
>>>
You can initialize the state of the Foo class from the Bar class and then append to the data like so:
class Foo(object):
def __init__(self):
self.data = "abc"
class Bar(Foo):
def __init__(self):
super(Bar, self).__init__()
self.data += "def"
When you instantiate an instance of Bar, the value of data that results is:
abcdef
You can refer to class variables of Foo from Bar's namespace via regular attribute access:
class Bar(Foo):
data = Foo.data + 'def'
I don't know if it's a good practice, but there's no reason for it to be bad.

confusion over inherited objects in abstract base class

Im a bit confused about inherited instance variables in ABCs. I have written an example to show my confusion. Class A needs a list which class B inherits but it must be an instance object rather than a class object. However class B also needs its own instance variable local. Can anyone set me straight?
#!python
from abc import ABCMeta, abstractmethod, abstractproperty
import unittest
class A(object):
__metaclass__ = ABCMeta
_internal = ['initialized']
#property
def internal(self):
return self._internal
def get_a(self):
return self._internal
#abstractmethod
def set_a(self, value):
pass
class B(A):
def __init__(self):
self.local = 'OK'
def get_local(self):
return self.local
def set_a(self, value):
self._internal.append(value)
class TestCase(unittest.TestCase):
def test_implementation(self):
self.assertEqual(['initialized'], B().get_a() ) # this passes but for wrong reason
b_used = B().set_a('used')
b_unused = B()
print "b_used.get_a() should return ['initialized','used']"
print "b_unused.get_a() should return ['initialized']"
print "b_used.get_local() should equal b_unused.get_local() = 'OK'"
self.assertEqual(['initialized'], b_unused.get_a()) # >> fails with ['initialized'] =! ['initialized', 'used']
self.assertNotEqual(b_unused.get_a(), b_used.get_a())
if __name__ == "__main__":
unittest.main()
The problem is that _internal is a class obj of class A. I need it to be an instance object of class B.
Thanks In advance
You should initialize instance attributes in __init__() and call the base class __init__() in B:
class A(object):
__metaclass__ = ABCMeta
def __init__(self):
self._internal = ['initialized']
...
class B(A):
def __init__(self):
A.__init__(self)
self.local = 'OK'
...
You should also fix your unit test:
class TestCase(unittest.TestCase):
def test_implementation(self):
self.assertEqual(['initialized'], B().get_a() ) # this passes but for wrong reason
b_used = B()
b_used.set_a('used')
b_unused = B()
...
Instance attributes should be defined in a method, eg __init__, by setting them on self.

Categories

Resources