On 23Jan2016 12:55, boB Stepp <robertvst...@gmail.com> wrote:
On Sat, Jan 23, 2016 at 3:30 AM, Cameron Simpson <c...@zip.com.au> wrote:
On 23Jan2016 01:52, boB Stepp <robertvst...@gmail.com> wrote:
I guess no matter how new one is to OOP, one nevertheless brings one's
preconceptions, however malformed, into the learning process. In my
case, one of mine was that once a class is coded, any given instance
of a class is forevermore responsible for managing its *internals*, so
that any change in these would be managed by the object when one of
its (meant to be) publicly available methods is called.
That is the pure OO way; and you can do things that way in Python, though
you can't _prevent_ direct access. However, it isn't the common way with
Python; generally with a Python class there are public attributes with
ordinary names; outsiders can consult them but generally should not change
them unless the doco says that is ok...
I'd like to focus on this last sentence. Are you suggesting that it
is "better" programming practice to code the class so that it has its
own publicly available methods to process its public attributes?
No, I'm suggesting that in a pure OO system, your only access to the internal
state of an object is via .get_thing() methods and the only way to set them is
via .set_thing(value) methods. In a system where outsiders cannot access
internal attributes, that provides a completely opaque layer where the object
mediates these actions to ensure correctness and where the internals are
invisible, allowing a complete change of implementation without breaking the
interface outsiders use.
And
that it is both good practice and Pythonic to allow outsiders to
freely read public attributes as needed?
Generally yes. As the author of the class, you need to decide what should be
visible (meaning "not have a leading underscore"). Of course it is _all_
visible, but when you give something a "public" name you are quietly implying
to outsiders that this is stable, and future implementations will continue to
preserve it.
You can be totally conservative of course and give all the internal state ._*
names. But an object with no attributes is generally not as useful. Note that
there's a grey area here: plenty of objects have methods which return values:
class O(object):
def __init__(self, name):
self._name = name
def name(self):
return self._name
o = O("foo")
print(o.name())
so you can keep the state "private" while presenting useful information via
methods. By having .name be a function, you are free to reimplement the class
using anther mechanism and outsiders will not have to change how they use
things:
class O(object):
def __init__(self, name):
# make a row in a database associating our id with this name
db.set_name(id(self), name)
def name(self):
# fetch the name back out of the database
n = db.get_name_by_id(id(self))
return n
I suspect there are probably better ways to do what I am trying to
demonstrate [I am trying to use a dict to simulate adding new instance
variables after the fact.],
You're aware that most objects have a .__dict__ attribute containing the
attributes? It is a dict, btw. So:
class O(object):
pass
>>> o=O()
>>> o.x=1
>>> o.__dict__
{'x': 1}
The book I am currently working through, "Python Crash Course",
despite its title, is oriented towards beginners to programming. It
does not cover dunder methods and attributes, other than __init__().
I am aware from following this list and reading portions of other
books, that they exist, but I have not gotten into the details of any
of them. So rewriting my class to use .__dict__, I have gotten:
class Dog(object):
def __init__(self, name, breed):
self.name = name
self.breed = breed
self.number_legs = 4
self.number_tails = 1
self.number_eyes = 2
def show_attributes(self):
print("The object,", self.name, "has the following attributes:")
for attribute_name, attribute_value in self.__dict__.items():
print(attribute_name, "=", attribute_value)
our_dog = Dog('Copper', 'beagle')
our_dog.show_attributes()
The object, Copper has the following attributes:
number_tails = 1
number_eyes = 2
breed = beagle
number_legs = 4
name = Copper
our_dog.unique_marking = 'blue right eye' # Adding a new attribute from
outside the class definition.
our_dog.show_attributes()
The object, Copper has the following attributes:
number_eyes = 2
breed = beagle
unique_marking = blue right eye
number_tails = 1
number_legs = 4
name = Copper
I have to say that this seems very simple and direct despite my
initial reluctance to assign new attributes to an object from outside
the class definition of that object.
Yes. No magic here, just exposed mechanism :-)
I still would like to do this via a method, but I am currently stuck
on how to replace ??? with the attribute name I would like to insert:
class Dog(object):
...
def add_attribute(self, attribute_name, attribute_value):
self.??? = attribute_value
self.__dict__[attribute_name] = attribute_value
If I write something like:
unique_marking = 'blue right eye'
our_dog.add_attribute(unique_marking, 'blue right eye)
I cannot get ??? to be unique_marking. Instead, show_attributes()
will give from .__dict__, 'attribute_name', instead of what I would
like to replace it with, unique_marking. But I have not given up yet!
The name is a string, so:
our_dog.add_attribute('unique_marking', 'blue right eye')
But since I am struggling to accomplish this, that argues (my
ignorance of Python aside) strongly for the clear, simple, direct
approach above that I did in my rewrite.
Alan will take you up on doing purer OO practices in Python.
An inside joke? Jedi Master Alan, please instruct me in the Pythonic
ways of OO purity!
No, but Alan has a far better handle on the concrete definitions of what is
purely OO practice and what is commonly mixed in. He is also a stronger
advocate of pure OO approaches than I.
Cheers,
Cameron Simpson <c...@zip.com.au>
_______________________________________________
Tutor maillist - Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor