Python syntax for namedtuple - python

I see that the Python syntax for a namedtuple is:
Point = namedtuple('Point', ['x', 'y'])
Why isn't it simpler like so:
Point = namedtuple(['x','y'])
Its less verbose,

In general, objects don't know what variables they are assigned to:
# Create three variables referring to an OrderedPair class
tmp = namedtuple('OrderedPair', ['x','y']) # create a new class with metadata
Point = tmp # assign the class to a variable
Coordinate = tmp # assign the class to another var
That's a problem for named tuples. We have to pass in the class name to the namedtuple() factory function so that the class can be given a useful name, docstring, and __repr__ all of which have the class name inside it.
These reason it seems strange to you is that normal function and class definitions are handled differently. Python has special syntax for def and class that not only creates functions and classes, but it assigns their metadata (name and docstring) and assigns the result to a variable.
Consider what def does:
def square(x):
'Return a value times itself'
return x * x
The keyword def takes care of several things for you (notice that the word "square" will be used twice):
tmp = lambda x: x*x # create a function object
tmp.__name__ = 'square' # assign its metadata
tmp.__doc__ = 'Return a value times itself'
square = tmp # assign the function to a variable
The same is also true for classes. The class keyword takes care of multiple actions that would otherwise repeat the class name:
class Dog(object):
def bark(self):
return 'Woof!'
The underlying steps repeat the class name (notice that the word "Dog" is used twice):
Dog = type('Dog', (object,), {'bark': lambda self: 'Woof'})
Named tuples don't have the advantage of a special keyword like def or class so it has to do the first to steps itself. The final step of assigning to a variable belongs to you. If you think about it, the named tuple way is the norm in Python while def and class are the exception:
survey_results = open('survey_results') # is this really a duplication?
company_db = sqlite3.connect('company.db') # is this really a duplication?
www_python_org = urllib.urlopen('http://www.python.org')
radius = property(radius)
You are not the first to notice this. PEP 359 that suggested we add a new keyword, make, that could allow any callable to gain the auto-assignment capabilities of def, class, and import.
make <callable> <name> <tuple>:
<block>
would be translated into the assignment:
<name> = <callable>("<name>", <tuple>, <namespace>)
In the end, Guido didn't like the "make" proposal because it caused more problems than it solved (after all, it only saves you from making a single variable assignment).
Hope that helps you see why the class name is written twice. It isn't really duplication. The string form of the class name is used to assign metadata when the object is created, and the separate variable assignment just gives you a way to refer to that object. While they are usually the same name, they don't have to be :-)

namedtuple is a factory, returning a class. Consider only expression:
namedtuple(['x','y'])
What would be the name of class returned by this expression?

The class should have a name and know it. And it doesn't see the variable you assign it to, so it can't use that. Plus you could call it something else or even nothing at all:
c = namedtuple('Point', ['x', 'y'])
do_something_with_this(namedtuple('Point', ['x', 'y']))
Speaking of simpler syntax, you can also write it like this:
namedtuple('Point', 'x y')

Because namedtuple is a function that returns a class. To do that, it is actually rendering a string template and calling eval. To build the string, it needs all the arguments beforehand.
You need to include the relevant context as arguments to namedtuple for that to happen. If you don't provide the class name argument, it would need to guess. Programming languages don't like to guess.
With the rules of the Python language, the namedtuple function within this expression..
>>> Point = namedtuple(['x','y'])
..doesn't have access to variable name (Point) that the result is stored in once the expression has been executed. It only has access to the elements of the list provided as its argument (and variables that have been defined earlier).

Related

What is the difference between assign and declare a variable in python? [duplicate]

I want to clarify how variables are declared in Python.
I have seen variable declaration as
class writer:
path = ""
sometimes, there is no explicit declaration but just initialization using __init__:
def __init__(self, name):
self.name = name
I understand the purpose of __init__, but is it advisable to declare variable in any other functions?
How can I create a variable to hold a custom type?
class writer:
path = "" # string value
customObj = ??
Okay, first things first.
There is no such thing as "variable declaration" or "variable initialization" in Python.
There is simply what we call "assignment", but should probably just call "naming".
Assignment means "this name on the left-hand side now refers to the result of evaluating the right-hand side, regardless of what it referred to before (if anything)".
foo = 'bar' # the name 'foo' is now a name for the string 'bar'
foo = 2 * 3 # the name 'foo' stops being a name for the string 'bar',
# and starts being a name for the integer 6, resulting from the multiplication
As such, Python's names (a better term than "variables", arguably) don't have associated types; the values do. You can re-apply the same name to anything regardless of its type, but the thing still has behaviour that's dependent upon its type. The name is simply a way to refer to the value (object). This answers your second question: You don't create variables to hold a custom type. You don't create variables to hold any particular type. You don't "create" variables at all. You give names to objects.
Second point: Python follows a very simple rule when it comes to classes, that is actually much more consistent than what languages like Java, C++ and C# do: everything declared inside the class block is part of the class. So, functions (def) written here are methods, i.e. part of the class object (not stored on a per-instance basis), just like in Java, C++ and C#; but other names here are also part of the class. Again, the names are just names, and they don't have associated types, and functions are objects too in Python. Thus:
class Example:
data = 42
def method(self): pass
Classes are objects too, in Python.
So now we have created an object named Example, which represents the class of all things that are Examples. This object has two user-supplied attributes (In C++, "members"; in C#, "fields or properties or methods"; in Java, "fields or methods"). One of them is named data, and it stores the integer value 42. The other is named method, and it stores a function object. (There are several more attributes that Python adds automatically.)
These attributes still aren't really part of the object, though. Fundamentally, an object is just a bundle of more names (the attribute names), until you get down to things that can't be divided up any more. Thus, values can be shared between different instances of a class, or even between objects of different classes, if you deliberately set that up.
Let's create an instance:
x = Example()
Now we have a separate object named x, which is an instance of Example. The data and method are not actually part of the object, but we can still look them up via x because of some magic that Python does behind the scenes. When we look up method, in particular, we will instead get a "bound method" (when we call it, x gets passed automatically as the self parameter, which cannot happen if we look up Example.method directly).
What happens when we try to use x.data?
When we examine it, it's looked up in the object first. If it's not found in the object, Python looks in the class.
However, when we assign to x.data, Python will create an attribute on the object. It will not replace the class' attribute.
This allows us to do object initialization. Python will automatically call the class' __init__ method on new instances when they are created, if present. In this method, we can simply assign to attributes to set initial values for that attribute on each object:
class Example:
name = "Ignored"
def __init__(self, name):
self.name = name
# rest as before
Now we must specify a name when we create an Example, and each instance has its own name. Python will ignore the class attribute Example.name whenever we look up the .name of an instance, because the instance's attribute will be found first.
One last caveat: modification (mutation) and assignment are different things!
In Python, strings are immutable. They cannot be modified. When you do:
a = 'hi '
b = a
a += 'mom'
You do not change the original 'hi ' string. That is impossible in Python. Instead, you create a new string 'hi mom', and cause a to stop being a name for 'hi ', and start being a name for 'hi mom' instead. We made b a name for 'hi ' as well, and after re-applying the a name, b is still a name for 'hi ', because 'hi ' still exists and has not been changed.
But lists can be changed:
a = [1, 2, 3]
b = a
a += [4]
Now b is [1, 2, 3, 4] as well, because we made b a name for the same thing that a named, and then we changed that thing. We did not create a new list for a to name, because Python simply treats += differently for lists.
This matters for objects because if you had a list as a class attribute, and used an instance to modify the list, then the change would be "seen" in all other instances. This is because (a) the data is actually part of the class object, and not any instance object; (b) because you were modifying the list and not doing a simple assignment, you did not create a new instance attribute hiding the class attribute.
This might be 6 years late, but in Python 3.5 and above, you can give a hint about a variable type like this:
variable_name: type_name
or this:
variable_name # type: shinyType
This hint has no effect in the core Python interpreter, but many tools will use it to aid the programmer in writing correct code.
So in your case(if you have a CustomObject class defined), you can do:
customObj: CustomObject
See this or that for more info.
There's no need to declare new variables in Python. If we're talking about variables in functions or modules, no declaration is needed. Just assign a value to a name where you need it: mymagic = "Magic". Variables in Python can hold values of any type, and you can't restrict that.
Your question specifically asks about classes, objects and instance variables though. The idiomatic way to create instance variables is in the __init__ method and nowhere else — while you could create new instance variables in other methods, or even in unrelated code, it's just a bad idea. It'll make your code hard to reason about or to maintain.
So for example:
class Thing(object):
def __init__(self, magic):
self.magic = magic
Easy. Now instances of this class have a magic attribute:
thingo = Thing("More magic")
# thingo.magic is now "More magic"
Creating variables in the namespace of the class itself leads to different behaviour altogether. It is functionally different, and you should only do it if you have a specific reason to. For example:
class Thing(object):
magic = "Magic"
def __init__(self):
pass
Now try:
thingo = Thing()
Thing.magic = 1
# thingo.magic is now 1
Or:
class Thing(object):
magic = ["More", "magic"]
def __init__(self):
pass
thing1 = Thing()
thing2 = Thing()
thing1.magic.append("here")
# thing1.magic AND thing2.magic is now ["More", "magic", "here"]
This is because the namespace of the class itself is different to the namespace of the objects created from it. I'll leave it to you to research that a bit more.
The take-home message is that idiomatic Python is to (a) initialise object attributes in your __init__ method, and (b) document the behaviour of your class as needed. You don't need to go to the trouble of full-blown Sphinx-level documentation for everything you ever write, but at least some comments about whatever details you or someone else might need to pick it up.
For scoping purpose, I use:
custom_object = None
Variables have scope, so yes it is appropriate to have variables that are specific to your function. You don't always have to be explicit about their definition; usually you can just use them. Only if you want to do something specific to the type of the variable, like append for a list, do you need to define them before you start using them. Typical example of this.
list = []
for i in stuff:
list.append(i)
By the way, this is not really a good way to setup the list. It would be better to say:
list = [i for i in stuff] # list comprehension
...but I digress.
Your other question.
The custom object should be a class itself.
class CustomObject(): # always capitalize the class name...this is not syntax, just style.
pass
customObj = CustomObject()
As of Python 3, you can explicitly declare variables by type.
For instance, to declare an integer one can do it as follows:
x: int = 3
or:
def f(x: int):
return x
see this question for more detailed info about it:
Explicitly declaring a variable type in Python

Can I declare a variable firmly typed as an integer in Python, like I do in Java? [duplicate]

I want to clarify how variables are declared in Python.
I have seen variable declaration as
class writer:
path = ""
sometimes, there is no explicit declaration but just initialization using __init__:
def __init__(self, name):
self.name = name
I understand the purpose of __init__, but is it advisable to declare variable in any other functions?
How can I create a variable to hold a custom type?
class writer:
path = "" # string value
customObj = ??
Okay, first things first.
There is no such thing as "variable declaration" or "variable initialization" in Python.
There is simply what we call "assignment", but should probably just call "naming".
Assignment means "this name on the left-hand side now refers to the result of evaluating the right-hand side, regardless of what it referred to before (if anything)".
foo = 'bar' # the name 'foo' is now a name for the string 'bar'
foo = 2 * 3 # the name 'foo' stops being a name for the string 'bar',
# and starts being a name for the integer 6, resulting from the multiplication
As such, Python's names (a better term than "variables", arguably) don't have associated types; the values do. You can re-apply the same name to anything regardless of its type, but the thing still has behaviour that's dependent upon its type. The name is simply a way to refer to the value (object). This answers your second question: You don't create variables to hold a custom type. You don't create variables to hold any particular type. You don't "create" variables at all. You give names to objects.
Second point: Python follows a very simple rule when it comes to classes, that is actually much more consistent than what languages like Java, C++ and C# do: everything declared inside the class block is part of the class. So, functions (def) written here are methods, i.e. part of the class object (not stored on a per-instance basis), just like in Java, C++ and C#; but other names here are also part of the class. Again, the names are just names, and they don't have associated types, and functions are objects too in Python. Thus:
class Example:
data = 42
def method(self): pass
Classes are objects too, in Python.
So now we have created an object named Example, which represents the class of all things that are Examples. This object has two user-supplied attributes (In C++, "members"; in C#, "fields or properties or methods"; in Java, "fields or methods"). One of them is named data, and it stores the integer value 42. The other is named method, and it stores a function object. (There are several more attributes that Python adds automatically.)
These attributes still aren't really part of the object, though. Fundamentally, an object is just a bundle of more names (the attribute names), until you get down to things that can't be divided up any more. Thus, values can be shared between different instances of a class, or even between objects of different classes, if you deliberately set that up.
Let's create an instance:
x = Example()
Now we have a separate object named x, which is an instance of Example. The data and method are not actually part of the object, but we can still look them up via x because of some magic that Python does behind the scenes. When we look up method, in particular, we will instead get a "bound method" (when we call it, x gets passed automatically as the self parameter, which cannot happen if we look up Example.method directly).
What happens when we try to use x.data?
When we examine it, it's looked up in the object first. If it's not found in the object, Python looks in the class.
However, when we assign to x.data, Python will create an attribute on the object. It will not replace the class' attribute.
This allows us to do object initialization. Python will automatically call the class' __init__ method on new instances when they are created, if present. In this method, we can simply assign to attributes to set initial values for that attribute on each object:
class Example:
name = "Ignored"
def __init__(self, name):
self.name = name
# rest as before
Now we must specify a name when we create an Example, and each instance has its own name. Python will ignore the class attribute Example.name whenever we look up the .name of an instance, because the instance's attribute will be found first.
One last caveat: modification (mutation) and assignment are different things!
In Python, strings are immutable. They cannot be modified. When you do:
a = 'hi '
b = a
a += 'mom'
You do not change the original 'hi ' string. That is impossible in Python. Instead, you create a new string 'hi mom', and cause a to stop being a name for 'hi ', and start being a name for 'hi mom' instead. We made b a name for 'hi ' as well, and after re-applying the a name, b is still a name for 'hi ', because 'hi ' still exists and has not been changed.
But lists can be changed:
a = [1, 2, 3]
b = a
a += [4]
Now b is [1, 2, 3, 4] as well, because we made b a name for the same thing that a named, and then we changed that thing. We did not create a new list for a to name, because Python simply treats += differently for lists.
This matters for objects because if you had a list as a class attribute, and used an instance to modify the list, then the change would be "seen" in all other instances. This is because (a) the data is actually part of the class object, and not any instance object; (b) because you were modifying the list and not doing a simple assignment, you did not create a new instance attribute hiding the class attribute.
This might be 6 years late, but in Python 3.5 and above, you can give a hint about a variable type like this:
variable_name: type_name
or this:
variable_name # type: shinyType
This hint has no effect in the core Python interpreter, but many tools will use it to aid the programmer in writing correct code.
So in your case(if you have a CustomObject class defined), you can do:
customObj: CustomObject
See this or that for more info.
There's no need to declare new variables in Python. If we're talking about variables in functions or modules, no declaration is needed. Just assign a value to a name where you need it: mymagic = "Magic". Variables in Python can hold values of any type, and you can't restrict that.
Your question specifically asks about classes, objects and instance variables though. The idiomatic way to create instance variables is in the __init__ method and nowhere else — while you could create new instance variables in other methods, or even in unrelated code, it's just a bad idea. It'll make your code hard to reason about or to maintain.
So for example:
class Thing(object):
def __init__(self, magic):
self.magic = magic
Easy. Now instances of this class have a magic attribute:
thingo = Thing("More magic")
# thingo.magic is now "More magic"
Creating variables in the namespace of the class itself leads to different behaviour altogether. It is functionally different, and you should only do it if you have a specific reason to. For example:
class Thing(object):
magic = "Magic"
def __init__(self):
pass
Now try:
thingo = Thing()
Thing.magic = 1
# thingo.magic is now 1
Or:
class Thing(object):
magic = ["More", "magic"]
def __init__(self):
pass
thing1 = Thing()
thing2 = Thing()
thing1.magic.append("here")
# thing1.magic AND thing2.magic is now ["More", "magic", "here"]
This is because the namespace of the class itself is different to the namespace of the objects created from it. I'll leave it to you to research that a bit more.
The take-home message is that idiomatic Python is to (a) initialise object attributes in your __init__ method, and (b) document the behaviour of your class as needed. You don't need to go to the trouble of full-blown Sphinx-level documentation for everything you ever write, but at least some comments about whatever details you or someone else might need to pick it up.
For scoping purpose, I use:
custom_object = None
Variables have scope, so yes it is appropriate to have variables that are specific to your function. You don't always have to be explicit about their definition; usually you can just use them. Only if you want to do something specific to the type of the variable, like append for a list, do you need to define them before you start using them. Typical example of this.
list = []
for i in stuff:
list.append(i)
By the way, this is not really a good way to setup the list. It would be better to say:
list = [i for i in stuff] # list comprehension
...but I digress.
Your other question.
The custom object should be a class itself.
class CustomObject(): # always capitalize the class name...this is not syntax, just style.
pass
customObj = CustomObject()
As of Python 3, you can explicitly declare variables by type.
For instance, to declare an integer one can do it as follows:
x: int = 3
or:
def f(x: int):
return x
see this question for more detailed info about it:
Explicitly declaring a variable type in Python

Assign attribute to function

This is probably very much a beginner question, but I have a question about attributes.
I have a module responsible for Google Docs API actions which contains functions for retrieving information. I would like to be able to refer to certain variables from these functions as attributes.
Here is an example:
Gdocs.py
def getRows():
rows = #action for getting rows
rowsText = #action for converting to text
General.py
import Gdocs
text = Gdocs.getRows.rowstext
I know the basic effect of passing variables can be achieved by just returning the values, but I would like to refer to them as attributes if possible. Simply put, my question is, how can you create an attribute of a function that I can reference in another .py document?
Thanks and sorry if it has been already answered, I did try to search but kept running nto very specific problems.
It sounds as if you want to return a result consisting of multiple parts. Don't use function attributes for this, return a new object that can be addressed via attributes instead. That'd make it thread-safe as well, as function attributes live in the same scope as the function object itself: as a global.
The standard library has a helpful class factory function for just such return values, collections.namedtuple():
from collections import namedtuple
Point = namedtuple('Point', 'x y')
def calculate_coordinates(foo, bar, baz):
return Point(42, 81)
The return value is a tuple subclass, so it can be addressed like a tuple (with indexing), it can be unpacked into separate values, or you can use attributes:
result = calculate_coordinates(spam, ham, eggs)
print result.x, result.y
or
res_x, res_y = calculate_coordinates(spam, ham, eggs)
all work.
While I understand what you said about not wanting a class for each function...
When you have a class, you can apply the #property decorator to functions.
This has the effect of allowing you to effectively create functions that exhibit behaviors but can be called just like attributes. In the following, if you wanted to produce a list of squares based on the input list, you could create a function with a verb-like name called create_list_of_squares(). But in this case, if you really want to keep the API simple and abstract away the mechanics behind the method and simply enable users to access the attributes 'squares', you can use a property decorator, like this...
class SquareList(list):
#property
def squares(self):
return [x ** 2 for x in self]
s = SquareList([1, 2, 3, 4])
print s.squares
which will yield:
[1, 4, 9, 16]
It's a little weird, but you can use staticmethod and classes to get what you want. To wit:
source: zattr2.py
class getRows(object):
#staticmethod
def rows(arg):
return [arg, arg]
#staticmethod
def rowsText(arg):
return repr(arg)
usage:
>>> import zattr2
>>> zattr2.getRows.rowsText('beer')
"'beer'"
See: https://docs.python.org/2/library/functions.html#staticmethod

"Private" attribute properties in Python

I'm relatively new to Python so I hope I haven't missed something, but here goes...
I'm trying to write a Python module, and I'd like to create a class with a "private" attribute that can (or maybe 'should') only be modified through one or more functions within the module. This is in an effort to make the module more robust, since setting of this attribute outside of these functions could lead to unwanted behaviour. For example, I might have:
A class that stores x and y values for a scatter plot, Data
A function to read x and y values from a file and store them in the class, read()
A function to plot them, plot()
In this case, I would prefer if the user wasn't able to do something like this:
data = Data()
read("file.csv", data)
data.x = [0, 3, 2, 6, 1]
plot(data)
I realise that adding a single leading underscore to the name indicates to the user that the attribute should not be changed, i.e. rename to _x and add a property decorator so that the user can access the value without feeling guilty. However, what if I wanted to add a setter property as well:
class Data(object):
_x = []
_y = []
#property
def x(self):
return self._x
#x.setter
def x(self, value):
# Do something with value
self._x = value
I'm now in the same position as I was before - the user can no longer directly access the attribute _x, but they can still set it using:
data.x = [0, 3, 2, 6, 1]
Ideally I'd rename the property function definitions to _x(), but this leads to confusion about what self._x actually means (depending on the order in which they are declared, this seems to result in either the setter being called recursively or the setter being ignored in favour of the attribute).
A couple of solutions I can think of:
Add a double leading underscore to the attribute, __x, so that the name becomes mangled and does not get confused with the setter function. As I understand it, this should be reserved for attributes that a class does not wish to share with possible subclasses, so I'm not sure if this is a legitimate use.
Rename the attribute, e.g. _x_stored. While this solves the problem completely, it makes the code harder to read and introduces naming convention issues - which attributes do I rename? just the ones that are relevant? just the ones that have properties? just the ones within this class?
Are either of the above solutions applicable? And if not, is there a better way to solve this problem?
Edit
Thanks for the responses so far. A few points thrown up by the comments:
I want to retain the extra logic that the setter property gives me - the # Do something with value section in the above example - so internally setting the attribute through direct access of self._x doesn't solve the problem.
Removing the setter property and creating a separate function _set_x() does solve the problem, but is not a very neat solution since it allows setting of _x in two different ways - either by calling that function or through direct access of self._x. I'd then have to keep track of which attributes should be set by their own (non-property) setter function and which should be modified through direct access. I'd probably rather use one of the solutions I suggested above, because even though they make a mess of the naming conventions within the class they are at least consistent in their use outside of the class, i.e. they all use the syntactical sugar of properties. If there's no way of doing this in a neater way then I guess I just have to choose the one that causes the least disruption.
If you want to discourage users from changing a property, but want it to be clear that they can read it, I'd use #property without providing a setter, similar to what you described earlier:
class Data(object):
def __init__(self):
self._x = []
self._y = []
#property
def x(self):
return self._x
#property
def y(self):
return self._x
I know you mention "What if I wanted to add a setter to the property?", but I guess I would counter that with: Why add the setter if you don't want your clients to be able to set the property? Internally, you can access self._x directly.
As for a client directly accessing _x or _y, any variable with an '_' prefix is understood to be "private" in Python, so you should trust your clients to obey that. If they don't obey that, and end up screwing things up, that's their own fault. This kind of mindset is counter to a many other languages (C++, Java, etc.) where keeping data private is considered very important, but Python's culture is just different in this regard.
Edit
One other note, since your private properties in this particular case are lists, which are mutable (unlike strings or ints, which are immutable), a client could end up changing them somewhat accidentally:
>>> d = Data()
>>> print d.x
['1', '2']
>>> l = d.x
>>> print l
['1', '2']
>>> l.append("3")
>>> print d.x
['1', '2', '3'] # Oops!
If you want to avoid this, you'd need your property to return a copy of the list:
#property
def x(self):
return list(self._x)
If you want less convoluted properties, that manage their own storage without leaving it open to "under the hood" alteration, you can define a class (similar to property) and use it to declare your class member:
I called mine 'Field':
class Field:
def __init__(self,default=None):
self.valueName = None # actual attribute name
self.default = default # type or value or lambda
if not callable(default): self.default = lambda:default
self._didSet = None # observers
self._willSet = None
def findName(self,owner): # find name of field
if self.valueName: return # once per field for class
for name,attr in owner.__dict__.items():
if attr is self:
self.valueName = f"<{name}>" # actual attribute name
break
def __get__(self,obj,owner=None): # generic getter
if not obj: return self
self.findName(owner or type(obj))
value = getattr(obj,self.valueName,self) # attribute from instance
if value is self:
value = self.default() # default value
setattr(obj,self.valueName,value) # set at 1st reference
return value
def __set__(self,obj,value): # generic setter
self.findName(type(obj))
if self._willSet: value = self._willSet(obj,value)
if self._didSet: oldValue = self.__get__(obj)
setattr(obj,self.valueName,value) # attribute in instance
if self._didSet: self._didSet(obj,oldValue)
def willSet(self,f): self._willSet = f
def didSet(self,f): self._didSet = f
usage:
class myClass:
lastName = Field("Doe")
firstName = Field("")
age = Field(int)
gender = Field("M")
relatives = Field(list)
#lastName.willSet
def _(self,newValue): # no function name needed
return newValue.capitalize()
#lastName.didSet
def _(self,oldValue): # no function name needed
print('last name changed from',oldValue,'to',self.lastName)
c = myClass()
c.firstName = "John"
c.lastName = "Smith"
# last name changed from Doe to Smith
c.relatives.extend(['Lucy','Frank'])
print(c.gender)
# M
print(c.__dict__)
# {'<lastName>': 'Smith', '<firstName>': 'John',
'<relatives>': ['Lucy', 'Frank'], '<gender>': 'M'}
Attributes added to the instance are not accessible from Python because they use identifiers that would not be valid in code.
Because you define default values at the class level, there is no need to set the field values in the constructor (although you could still do it as needed)
Field values are only added as instance attributes when they are referenced making the instance creation process more efficient.
Note that my actual Field class is a lot more sophisticated and supports change tracking, more observer functions, type checking, and read-only/calculated fields. I boiled it down to essentials for this response
For Private/Public method protection, you may want to look at this answer

access class attributes from within other attribute definitions in python

I'm writing some code, in which a class defines a few nested classes (to keep things neater and grouped by purpose). I want the nested classes to use the attribute of the enclosing class (see below). This attribute would provide a default value that is set on top of the enclosing class and not a 'magic number'.
class Progress_indicator:
#use this attribute to provide a default value
number_of_dots=5
class Dots:
#the attribute above provides a default value here:
def __init__(self, number = Progress_indicator.number_of_dots):
self._current=0
self._number_of_dots=number
Now, when the above code is run I get:
NameError: name 'Progress_indicator' is not defined
from the line containing __init__.
I understand that this is due to python defining a namespace as it sees a class declaration but actually assigning that namespace to a name (Progress_indicator in this case) after the full class declaration is processed (i.e. the indented block of code is left). Redefining __init__ to use self.number_of_dots instead of Progress_indicator.number_of_dots generates the same error (as there is no inheritance here). All references I tried (books, and lots of web) suggest that the above should work. But it doesn't. Is there a neat way of accessing the attribute defined in the enclosing class to provide a default value for a function parameter as above?
I'm using python3.2.
Use None as a placeholder value, and substitute for default if it hasn't been provided.
class Indicator:
number_of_dots = 5
class Dots:
def __init__(self, number = None):
if number is None:
number = Indicator.number_of_dots
self._current = 0
self._number_of_dots = number
It might be bit wordier, but you'll avoid namespacing issues. Alternatively you can use self._number_of_dots = number or Indicator.number_of_dots if 0 is not a valid value that can be used as an argument.
You could also do def __init__(self, **kwargs) and then kwargs.pop('number', Indicator.number_of_dots), but that might be less clear.

Categories

Resources