This question already has answers here:
How to avoid having class data shared among instances?
(7 answers)
Closed 9 years ago.
I can create class definition dynamically, like there:
class_name = 'Human'
base_classes = (object,)
attributes = {'name':'',
'books':list(),
'say_hello':lambda self: sys.stdout.write('Hello!')}
Human = type(class_name, base_classes, attributes)
uzumaxy = Human()
uzumaxy.name = 'Maxim'
uzumaxy.books.append('Programming via .NET')
print(uzumaxy.name) # Out: "Maxim"
print(uzumaxy.books) # Out: "['Programming via .NET']"
grandrey = Human()
grandrey.name = 'Andrey'
grandrey.books.append('Programming via python')
print(grandrey.name) # Out: "Andrey"
print(uzumaxy.name) # Out: "Maxim"
print(grandrey.books) # Out: "['Programming via .NET', 'Programming via python']"
print(uzumaxy.books) # Out: "['Programming via .NET', 'Programming via python']", but i'm expecting: "['Programming via .NET']"
Seems, attribute "name" is instance-level, but why attribute "books" is class-level?
How I can dynamically create definition of type with instance-level attributes? Thx for help.
Actually, both name and books are class-level. It's just that strings are immutable, so when you use uzumaxy.name = "Maxim", you're adding a new attribute called name hiding the class name, while for uzumaxy.books.append("Programming via .NET"), you're accessing the existing (class) books and modifying it. Your code is equivalent to this:
class Human(object):
name = ''
books = []
def say_hello(self):
sys.stdout.write("Hello!")
Note the same behavior. Traditionally, we'd fix that by writing Human like this:
class Human(object):
def __init__(self):
self.name = ''
self.books = []
def say_hello(self):
sys.stdout.write("Hello!")
Now each instance has its own name and books. To do this with a dynamically-created type, you do essentially the same thing, giving it an __init__:
def init_human(self):
self.name = ''
self.books = []
attributes = { '__init__': init_human,
'say_hello': lambda self: sys.stdout.write("Hello!") }
They're both class-level. name is simply immutable, so it doesn't look class-level at first glance. Most attempts to modify it will create a new instance-level attribute with the same name.
Just like when writing a class the normal way, you need to create instance attributes in the constructor:
def __init__(self):
self.name = ''
self.books = []
def say_hello(self):
# This prints a newline. The original didn't.
print 'Hello!'
Human = type('Human', (object,), {
'__init__': __init__,
'say_hello': say_hello,
})
Related
As an attribute to a certain class, I'm instantiating a bunch of objects of another class. My problem is that they have ugly names for memory addresses. How do I give them proper names?
class CaseReader(object):
def __init__(self, path):
cases_paths = glob(path + '//*')
cases_names = os.listdir(path)
self.case = [Case(i) for i in cases_paths]
Upon running:
a = CaseReader(path)
a
Out[4]: <__main__.CaseReader at 0x1c6dfc7fa88>
a.case
Out[5]:
[<__main__.Case at 0x1c6dfc99fc8>,
<__main__.Case at 0x1c6dfc99dc8>,
<__main__.Case at 0x1c6dfcaf3c8>,
<__main__.Case at 0x1c6dfcaf448>,
<__main__.Case at 0x1c6dfcaf208>]
Overwrite the __str__ function in the class definition and print what ever attributes you want to see, when you print the reference of the object.
Sample Code
class A:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
I'd like to know if there's a way to get class instance variables that are inside an init.
I've seen something verify close what I am looking for except that I am looking for a way to get instance variables and not class variables.
Linked subject: Looping over class variable's attributes in python
Say I have a class such as:
class Identity:
table = "tb_identity"
def __init__(self, id="", app_name="", app_code="", state="", criticality=""):
self.id = id
self.app_name = trigram_name
self.app_code = trigram_irt
self.state = state
self.criticality = criticality
I'd like to be able to get a list of with instance variables name like:
["id","app_name","app_code","state","criticality"]
With something such as :
members = [getattr(Identity,attr) for attr in dir(Identity) if not attr.startswith("__")]
Im only getting "tb_identity" and not even "table".
But that's not the main problem, I am looking for something like:
["id","app_name","app_code","state","criticality"]
Is there any proper way to get these variables inside init ?
Thank you for your time.
Edit: Any way of doing this without instanciation?
Try
class Identity:
table = "tb_identity"
def __init__(self, id="", app_name="", app_code="", state="", criticality=""):
self.id = id
self.app_name = 'trigram_name'
self.app_code = 'trigram_irt'
self.state = state
self.criticality = criticality
print(self.__dict__.keys())
i = Identity()
Output
dict_keys(['id', 'app_name', 'app_code', 'state', 'criticality'])
I'm struggling to solve with this problem.
I'd like to have the name variable to be like a pointer to the value of self.model.name. If the value of _class.model.name is changed the _class.name should change accordingly.
Can't find a way to basically map dynamically the Class attributes with any Model attributes without inheriting.
class Model(object):
name = 'foo'
parent = 'foo_p'
class Class(object):
model_class = Model
def __init__(self):
self.model = self.model_class()
setattr(self, 'name', self.model.name)
_class = Class()
print _class.model.name # foo
_class.model.name = 'foo_1'
print _class.name # this should be foo_1
Thanks!
Use a property to create a single dynamically computed attributes:
class Class(object):
_model_class = Model
#property
def name(self):
return Class._model_class.name
This causes all instances of Class to run the name method whenever the attribute name is looked up. This allows the value to be dynamically computed on each lookup.
_class = Class()
print(_class.name) # 'foo'
Model.name = 'bar'
print(_class.name) # 'bar'
If you want to dynamically fetch many or all attributes from somewhere else, consider using __getattr__:
class Class(object):
_model_class = Model
def __getattr__(self, name):
# name is a *string* containing the name of the attribute to fetch
return getattr(Class._model_class, name)
The __getattr__ is only triggered for attributes that are not on the class/instance. This makes it rather straightforward to use with manually defined attributes. Note that you can use arbitrary code to restrict what is fetched - e.g. raise AttributeError if name is not in some whitelist.
Along with MisterMiyagi's answer - but in case you want to still want to keep the concerns separated (even though it really doesn't seem like you do)
class Class(object):
model_class = Model
def __init__(self):
self.model = self.model_class()
setattr(self, 'name', self.model.name)
# This is what we'll be changing
#property
def model_name(self):
return self.model.name
# This method will get called whenever we change the model_name
#model_name.setter
def model_name(self, new_name):
self.model.name = new_name
self.name = new_name
_class = Class()
_class.model_name # foo
_class.model_name = "bar" # both _class.model.name == bar and _class.name == bar now
I wrote a code:
class NewsStory(object):
def __init__(self, guid, title, subject, summary, link):
NewsStory.guid = guid
NewsStory.title = title
NewsStory.subject = subject
NewsStory.summary = summary
NewsStory.link = link
def getGuid(self):
return self.guid
def getTitle(self):
return self.title
def getSubject(self):
return self.subject
def getSummary(self):
return self.summary
def getLink(self):
return self.link
When I added an instance as:
test = NewsStory('foo', 'myTitle', 'mySubject', 'some long summary', 'www.example.com')
print test.getGuid() gives me foo, which is correct. However, if I continuously created two instances:
test = NewsStory('foo', 'myTitle', 'mySubject', 'some long summary', 'www.example.com')
test1 = NewsStory('foo1', 'myTitle1', 'mySubject1', 'some long summary1', 'www.example1.com')
both print test.getGuid() and print test1.getGuid() gave me foo1 but no foo. Why does it happen? And is there a method that I can modify my class definition or functions inside the class to avoid the new created instance overwriting the old one?
Thank you.
You'll need to make those variables in your __init__ function instance variables instead of class variables.
Instance variables look like this:
self.guid = guid
Class variables look like this:
NewsStory.guid = guid
Class variables are the same for all members of the class, but instance variables are unique to that instance of the class.
The __init__ method is called after an instance of the class is created. The first argument, called self by convention, is the instance of the class. NewsStory is the class itself.
In your code, you're creating class variables. You want instance variables:
self.guid = guid
You are modifying class variables, which are common to all the objects. What you should do is to create those variables in the object, like this
self.guid = guid
self.title = title
self.subject = subject
self.summary = summary
self.link = link
I can't understand how works the code below:
class Host:
name = None
fileList = []
def __init__(self, hostName):
self.name = hostName
def AddInfo(self,file):
self.fileList.append(file)
def Cleanup(self):
self.fileList = []
I create 2 instances:
h1 = Host("hostName1")
h1.AddInfo("h1")
h1.Cleanup()
print h1.fileList, Host.fileList
h2 = Host("hostName2")
h2.AddInfo("h2")
print h2.fileList, Host.fileList
the result is:
h1.fileList = [], Host.fileList = ['h1']
h2.fileList = ['h1', 'h2'], Host.fileList = ['h1', 'h2']
why Host.fileList value is changed - I assigned new values to the instance only? why h2.fileList has such value - I was expecting ['h2'] here?
This is basic Python (as in "Python is not Java").
Attributes declared at the class level are class attributes, not instance attributes. To declare an instance attribute, assign directly to self inside a method, as you do with self.name in __init__.
This is probably easist to see by stepping through your code:
h1 = Host("hostName1") #After __init__:h1.filelist -> Host.filelist
h1.AddInfo("h1") #After AddInfo: h1.filelist -> Host.filelist
h1.Cleanup() #After Cleanup: h1.filelist is a new list
h2 = Host("hostName2") #After __init__:h2.filelist -> Host.filelist (which already has 'h1' inserted)
h2.AddInfo("h2") #After AddInfo: h2.filelist -> Host.filelist
Inside your methods, when using self.filelist, python looks to see if the instance has a filelist attribute first. If the instance doesn't have that attribute, python looks for the attribute on the class the instance belongs to. when you do self.filelist=[] in Cleanup, you are giving the instance the attribute filelist.
Variables in class scope are defined by class. You should create your variables in __init__.
def __init(self, hostName):
self.name = hostName
self.fileList = []