#property not working after adding __slots__ - python

How can I get slots to work with #property for the class below. I have several thousand instances of below class which is causing memory issues and so I added the slots
I created instances with data and then add location information later to the instances.
After adding slots my instance creation is not working and I am getting the following error
AttributeError: 'Host' object has no attribute '_location'
class Host(object):
__slots__ = ['data', 'location']
def __init__(self, data, location=''):
self.data = data
self.location = location
#property
def location(self):
return self._location
#location.setter
def location(self, value):
self._location = value.lower()
def __repr__(self):
if self.location == '':
self.loc = 'Not Found'
else:
self.loc = self.location
return 'Host(name={}, location={})'.format(self.name, self.loc)

__slots__ works by creating descriptors on the class that have direct access to the in-memory data structure of your instance. You are masking the location descriptor with your property object, and you defined a new attribute _location than is not in the slots.
Make _location the slot (as that is the attribute you are actually storing):
class Host(object):
__slots__ = ['data', '_location']
The location property (also a descriptor object) can then properly assign to self._location, an attribute backed by the slot descriptor.
Note that you do not need to use self.loc in the __repr__, just make that a local variable instead. You also are trying to use a self.name attribute which doesn't exist; it is not clear what value that is supposed to be however:
def __repr__(self):
loc = self.location or 'Not Found'
name = self.data['name'] # or some other expression
return 'Host(name={}, location={})'.format(name, loc)

The definition for __slots__ should have the names of the underlying attributes that will store the data referenced by your properties. In the example below, name mangling is invoked for variables that should not be accessed outside of the class. The code is similar to yours and has no errors according to the PEP8 online website.
#! /usr/bin/env python3
def main():
print(Host('Hello, world!', 'Earth'))
print(Host('Hello, Xyz!'))
class Host:
__slots__ = '__name', '__location'
def __init__(self, name, location=''):
self.name = name
self.location = location
def __repr__(self):
return '{!s}({!r}, {!r})'.format(
type(self).__name__,
self.name,
self.location
)
#property
def name(self):
return self.__name
#name.setter
def name(self, value):
self.__name = value
#property
def location(self):
return self.__location
#location.setter
def location(self, value):
self.__location = value.casefold() if value else 'Not Found'
if __name__ == '__main__':
main()

Related

What is the difference between readable property method and a callable function that is just returns the data as a property can?

I have a property that returns list of names with "ash" in it
class BaseClass(object):
def __init__(self):
self.filter_key = ""
self.name = ""
def filter_names(self, filter_key):
self.filter_key = filter_key
#property
def student_names(self):
return self.names
def callable_function_names(self):
return names
and then student class that inherits BaseClass
class StudentClass(BaseClass):
#property
def student_names(self):
names = super(StudentClass, self).student_names
return [name for name in names if self.filter_students in name]
#property
def filter_key(self):
"""Gets """
return self.filter_key
#slot_key.setter
def filter_key(self, key):
"""Sets name filter"""
self.filter_names(key)
# or by doing :
def callable_function_names(self):
names = super(StudentClass, self).callable_function_names()
return [name for name in names if self.filter_students in name]
So if I create obj of the student class.
studentclsObj = StudentClass()
studentclsObj.filter_key = "ash"
print studentclsObj.student_names
print studentclsObj.callable_function_names()
I can achieve the same result with both above prints, is there any difference and what is preferred and right way to do ?
One use case of properties is not breaking API. This is one of main strengths of python IMO. You can take a function, make transform it in a callable object, add new functionality without breaking old code, now the property
I see three main uses of properties over attributes,
Read only attributes
Is easy to create read only attributes with properties. They are non verbose, self documenting and simple
class Foo:
def __init__(self, bar):
self._bar = bar
#property
def bar(self):
return self._bar
Validation on writable properties
class Foo:
def __init__(self, bar):
self._bar = bar
#property
def bar(self):
return self._bar
#bar.setter
def bar(self, val):
if valid(val):
self._bar = val
This is a kind of defensive programming
Keep API compatibility
Imagine that you have a class for a bank account, with
a balance property
class BankAccount:
def __init__(self):
self.balance = 0
You have this code and it works fine. But know your client
says, I need you to log every balance lookup. You can replace
the attribute by a property without breaking old code
class BankAccount:
def __init__(self):
self._balance = 0
#property
def balance(self):
self.log_balance_read()
return self._balance
There is no difference between a property and a method which return the same value. Go for the simpler, use method for actions and state changes and attributes for real attributes, if you need to add logic to attribute lookup, python will let you do it

Python class objectivity convention

Is it a good style to create classes like that ? I read the PEP8 document but I didn't saw any good example. If not how is it a proper way ? Thanks for any answers.
class Zone:
def __init__(self, index=None, name=None):
self._index = index
self._name = name
#property
def index(self):
return self._index
#property
def name(self):
return self._name
#index.setter
def index(self, index):
self._index = index
#name.setter
def name(self, name):
self._name = name
Your setters and getters don't do anything. With your implementation, the user of this class does this:
z = Zone()
z.name = 'foo'
print(z.name)
Compare to this implementation:
class Zone:
def __init__(self, index=None, name=None):
self.index = index
self.name = name
z = Zone()
z.name = 'foo'
print(z.name)
It works exactly the same with a lot less code.
Unless you do anything in your setters and/or getters, you don't need them.
If what you intend doing is encapsulating your data and setting it with setters and getting it with getters, then what you did will not be helpful. you declared the _name and _index as protected, it does not mean it cannot be accessed by extenal functions, so functions outside the class can easily access and change them, making your getter and setter to be useless.
However, you can declare them as private by using one additional underscore in front, so that your property class will be removed and then the setters class will be useful, it will no longer be accessed by external functions.
class Zone:
def __init__(self,index=None,name=None):
self.__index = index
self.__name = name
def index(self, index):
self.__index = index
def name(self, name):
self.__name = name
def get_name(self):
return self.__name
zone=Zone()
zone.name('ben')
print(zone.get_name())
>>>ben
print(zone.__name)
>>> AttributeError: 'Zone' object has no attribute '__name'

Python unit testing Class properties

I am trying to figure out if there's a way to (unit test) verify that the property and the setter is actually called to set the name attribute.
class DummyName:
def __init__(self):
self.name = ''
#property
def name(self):
return self.name
#name.setter
def name(self, name):
if not isinstance(name, basestring):
raise Exception('Name must be a string.')
self.name = name
Trying to do something like this...
#mock.patch.object(DummyName, 'name', new_callable=PropertyMock)
def testNameProperty(self, mock_name):
MockName = Mock()
mock_name.return_value = MockName
dummyName = DummyName()
dummyName.name = 'test_name'
# assert setter is called to set the name
# assert name is called to get the name
# assert name is 'test_name'
Seems like name() and setter are never accessed. the Anyone has a better idea? Thanks!
By using mocks like that you've overwritten the code you're trying to test. Mocks are for calls that are external to the code under test.
An appropriate test for this code is to assert that the exception is raised if you pass something that isn't a string.
def testNameProperty(self):
dummyName = DummyName()
with self.assertRaises(Exception):
dummyName.name = 12345
Your class needs to inherit from object.
class DummyName(object):
def __init__(self):
self._name = ''
#property
def name(self):
return self._name
#name.setter
def name(self, name):
if not isinstance(name, basestring):
raise Exception('Name must be a string.')
self._name = name
You also need to use different variables for the name inside the class, or you'll hit maximum recursion.

Python Object not callable [duplicate]

This question already has an answer here:
'int' object is not callable in python
(1 answer)
Closed 6 years ago.
I'm new to python:
Created a Class:
class NodeStruct:
"""Struct to hold node data for search trees"""
def __init__(self, name, children, parent_name, edge_weight):
self.name = name
self.childNodes = children
self.parent = parent_name
self.weight = edge_weight
def name(self):
return self.name
def parent(self):
return self.parent
def path_cost(self):
return self.weight
def children(self):
return self.childNodes
def child_keys(self):
return self.childNodes.keys()
Instantiate:
this_node = NodeStruct(start, problem[start], 'root', 0)
The Problem: when I make a call to name()
name = this_node.name()
I get the following error:
TypeError: 'str' object is not callable
Looks like it should be straight forward... What am I missing?
When you define your class, name is a function. As soon as you instantiate it, though, __init__ is called, and name is immediately set to whatever you pass in (a string in this case). The names of functions are not kept separate from the names of other objects. Use a unique name.
Which one of the names do you expect this_node.name() to find?
self.name = name
# ...
def name(self):
return self.name
The solution is likely to change the name of the attribute to self._name:
self._name = name
# ...
def name(self):
return self._name

Listing instance attributes with __dict__

Hi I am trying to use composition to create a new class using instances of another class when I try to turn the new object into a dictionary using __dict__, it's shows me <__main__.myobjec object at 0x00000000029CA908>, not sure am I using the __dict__ incorrectly though I have heard its related to new classes, any help greatly appreciated.
class User:
def __init__(self, name, job=None):
self.name = name
self.job = job
class Customer(User):
_ID = 100
def __init__(self, name, job=None):
self.name = name
self.job = job
class Account:
_ID = 0
def __init__(self, name, job=None):
self.customer = Customer(name , "Customer")
def __getattr__(self, attr):
return getattr(self.customer, attr)
>>> A = Account("Abdi")
>>> A.__dict__
{'customer': <__main__.Customer object at 0x109fdbfc8>}
>>>
You need to implement the __repr__ method to represent all of the instances of the Customer class.
def __repr__(self): return repr(self.__dict__) # the dictionary of attributes in __repr__

Categories

Resources