Python Programming for Biology: Bioinformatics and Beyond



Download 7,75 Mb.
Pdf ko'rish
bet107/514
Sana30.12.2021
Hajmi7,75 Mb.
#91066
1   ...   103   104   105   106   107   108   109   110   ...   514
Bog'liq
[Tim J. Stevens, Wayne Boucher] Python Programming

Refined implementation

Getter and setter functions

Given  the  way  we  have  implemented  the  classes,  we  can  directly  access  and  manipulate

the  class  attributes.  For  example,  if  we  have  a  structure  object  we  can  get  the  attribute

values:


name = structure.name

conf = structure.conformation

We can also change these values:

structure.name = 'new name'

structure.conformation = 235

Although  the  former  seems  harmless  enough,  the  latter  might  be  a  bad  idea.  It’s




possible  the  application  might  rely  on  these  two  attributes  not  changing.  Or  even  if  the

information is allowed to change, it’s possible that the values being set might be illegal in

some way. For example, in the constructor we checked that name was not an empty string

and that is not done here, so it is perfectly possible that we could do:

structure.name = ''

The standard way to deal with situations like this in most modern computer languages

(e.g. Java) is to design the class so that attributes are private and thus not accessible in this

way.  Instead  we  would  have  getters  and  possibly  setters,  which  are  functions  that  allow

access to query and redefine the relevant information, but in a guarded way. For example,

here we could have:

class Structure:

# …


def getName(self):

return self.name

def setName(self, name):

if not name:

raise Exception('name must be set to non-empty string')

self.name = name

We see that the setter function setName() has some validity checking in it. Access then

becomes:


name = structure.getName()

structure.setName('new name')

If  for  some  reason  we  thought  that  the  name  should  not  be  changeable,  we  would

simply  not  provide  the  setter  function,  so  only  the  getter  function  would  exist.  This

approach  still  has  a  couple  of  problems.  First  of  all,  this  access  is  rather  verbose  to  use.

For example, instead of:

residue.chain.structure.name = 'new name'

we would have to do:

residue.getChain().getStructure().setName('new name')

Secondly, as it stands, the attribute name is still accessible, so someone could still use

direct  access,  albeit  deliberately  or  by  mistake.  In  Python,  nothing  can  be  totally  hidden

from the user in the implementation of a class. However, there are ways to signal the clear

intent to disallow access. Instead of using name for the attribute we could use _name,  so

starting with an underscore. Then the getter and setter become:

class Structure:

# …


def getName(self):

return self._name

def setName(self, name):

if not name:




raise Exception('name must be set to non-empty string')

self._name = name

With  this  change  structure.name  no  longer  works,  but  structure._name  does,  although

the underscore in front is a warning that this is not intended to be used. Another alternative

is  to  use  __name,  starting  with  two  underscores.  In  this  case  structure.__name  does  not

work,  and  trying  to  use  it  results  in  an  AttributeError  exception.  This  is  a  bit  of  Python

‘magic’  to  make  the  attribute  somewhat  private.  However,  it  turns  out  that  a  determined

person could still get at the attribute, just using another bit of Python magic: an underscore

and the class name have to be joined to the beginning of the attribute name. Thus, here the

access would be via structure._Structure__name. There is no real privacy in Python.

Setter  functions  often  have  validity  checking  in  them,  so  they  seem  to  serve  a  useful

purpose. On the other hand getter functions are often very boring, and just give back an

attribute,  so  they  often  seem  to  serve  little  purpose.  Nevertheless,  there  are  a  few  clear

situations  when  they  actually  do  something  useful.  The  first  case  is  when  the  high

cardinality of the attribute is greater than 1; we have a collection of items. Returning to an

example of this from earlier in the chapter, we could have decided that the Structure class

has pdbIds, instead of just one pdbId. The naïve getter function would simply be:

class Structure:

# …

def getPdbIds(self):



return self.pdbIds

Unlike strings and  integers, collections are  modifiable; their contents  can be changed.

Thus, returning the value in this way would allow the person using the function to directly

manipulate the collection, which may cause problems for the objects:

pdbIds = structure.getPdbIds()

del pdbIds[0] # delete first one; changes structure.pdbIds

An  alternative  implementation  could  return  a  copy  of  the  collection  instead  of  the

internal attribute:

class Structure:

# …


def getPdbIds(self):

return list(self.pdbIds)

Here the list that is returned from the function can be manipulated without any harm:

pdbIds = structure.getPdbIds()

del pdbIds[0] # does not change structure.pdbIds

Another  case  when  a  getter  function  is  useful  is  when  the  associated  value  is  not

directly stored in the object, but instead is calculated on-the-fly. For example, suppose we

wanted  to  have  a  function,  structure.getMass(),  which  returns  the  molecular  mass  of  the

structure. We might choose not to store the mass at all, but instead do something like:

class Structure:

# …

def getMass(self):




mass = 0

for chain in self.chains:

mass += chain.getMass()

return mass

and  in  turn  the  chain.getMass()  could  use  residue.getMass()  and  that  could  use

atom.getMass(). Even the Atom class might not store the mass but instead calculate it on-

the-fly using a dictionary based on the element.

Our  final  example  of  useful  getter  functions  illustrates  that  the  underlying

implementation of a class can change, but any code that uses a getter function instead of

direct  attribute  access  does  not  itself  have  to  change.  Here,  we  might  decide  that

calculating  the  mass  on-the-fly  is  a  slow  procedure,  so  we  want  to  calculate  it  once  and

then cache the result:

9

class Structure:



# …

def getMass(self):

if hasattr(self, 'mass'):

# already been calculated so just return value

return self.mass

# not been calculated yet so do it

mass = 0

for chain in self.chains:

mass += chain.getMass()

self.mass = mass # cache for next time

return mass

This  has  changed  how  the  class  is  implemented,  but  all  code  that  uses  the  getMass()

function is unaffected.


Download 7,75 Mb.

Do'stlaringiz bilan baham:
1   ...   103   104   105   106   107   108   109   110   ...   514




Ma'lumotlar bazasi mualliflik huquqi bilan himoyalangan ©hozir.org 2024
ma'muriyatiga murojaat qiling

kiriting | ro'yxatdan o'tish
    Bosh sahifa
юртда тантана
Боғда битган
Бугун юртда
Эшитганлар жилманглар
Эшитмадим деманглар
битган бодомлар
Yangiariq tumani
qitish marakazi
Raqamli texnologiyalar
ilishida muhokamadan
tasdiqqa tavsiya
tavsiya etilgan
iqtisodiyot kafedrasi
steiermarkischen landesregierung
asarlaringizni yuboring
o'zingizning asarlaringizni
Iltimos faqat
faqat o'zingizning
steierm rkischen
landesregierung fachabteilung
rkischen landesregierung
hamshira loyihasi
loyihasi mavsum
faolyatining oqibatlari
asosiy adabiyotlar
fakulteti ahborot
ahborot havfsizligi
havfsizligi kafedrasi
fanidan bo’yicha
fakulteti iqtisodiyot
boshqaruv fakulteti
chiqarishda boshqaruv
ishlab chiqarishda
iqtisodiyot fakultet
multiservis tarmoqlari
fanidan asosiy
Uzbek fanidan
mavzulari potok
asosidagi multiservis
'aliyyil a'ziym
billahil 'aliyyil
illaa billahil
quvvata illaa
falah' deganida
Kompyuter savodxonligi
bo’yicha mustaqil
'alal falah'
Hayya 'alal
'alas soloh
Hayya 'alas
mavsum boyicha


yuklab olish