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

Reply via email to