[Tutor] string not found in variable

dn PyTutor at DancesWithMice.info
Mon Mar 22 22:48:16 EDT 2021


If I suffered from any higher-level of paranoia I'd be sure that @Alan
was 'pressing my buttons'...


On 23/03/2021 14.20, Alan Gauld via Tutor wrote:
> On 22/03/2021 23:18, jark AJ wrote:
> 
>> 1) At what point you would have used classes in this case or what could be
>> the use case that would decide us to choose classes over functions
> 
> A function is a single operation. It performs a task.
> It takes in data and returns a result. It does not have
> state(generator functions being a notable exception!)
> So if you are thinking of a class to perform a single
> task you probably want a function instead.
> 
> Classes represent objects. As such they have *attributes*
> that manage the *internal state*. They also have a set
> of *operations* by which the outside world communicates
> with the objects (and indeed by which the *objects can
> communicate* with each other).
> 
> So if, in your solution, you have some state data that
> needs to have  various things done to it, especially if
> those things are done over time, then a class is appropriate.
> 
> Also, if you need several instances of something
> represented by a common group of data attributes
> then you probably want a class (If there are no
> operations then maybe you just need a dictionary plus
> factory function instead)
> 
> To repeat the aphorism:
> "If you have a class with a single method plus init()
> then you really wanted a function."
> 
> There is a lot of programming overhead in creating a
> class, and a lot of runtime overhead in instantiating
> a class and calling its methods (compared to calling
> a function). Don't create classes needlessly. Classes
> do not necessarily imply better code. When they are
> appropriate they are very useful, but only when
> appropriate.


I agree with the aphorism, and @wlfraed's comment (earlier in thread)
that creating a class only to call its methods one line after another
and/or only once, should be viewed with suspicion!


In this application, the loop is to hammer on the door of the one web
site, five times, apparently in a bid to detect packet-loss.
Accordingly, the instantiation (inclusive of the URL) is required only
once.

Also, please observe that the post-processing of any feedback is
simplistic (the OP may have edited to minimal-code for posting).

Thus, apart from one aspect, which will be discussed in a moment, a
function is likely 'cleaner', and almost certainly more-efficient!


When I construct a similar facility, over and over, in multiple
applications, two things happen:

- I become bored/lazy, ie not this again! and
- my old-age/advanced-decrepitude/failing-memory means that I forget
'stuff'.


Accordingly, I do have a class which acts as a 'wrapper' around the
'common bits that I use' of the (perfectly reasonable) subprocess
library. Because this custom-module has been constructed and documented
to my satisfaction (?peculiarities), the second issue has been addressed
with a bunch of reminders in the form of 'give me a BASH command here'
or 'what's the URL?' parameters, or outright docstring 'reminders', ie
tell me again: why do I need to do this.

Secondly, or rather primarily, I sat down and worked-out where errors
might occur, what the options are for gathering feedback, etc. Thus,
whacking a new application's command through the class means that I
'inherit' all that research and checking, without having to remember it
(and without risk of 'forgetting' to program[me] defensively) this time
and every time.
(one of my aphorisms: you have to work hard to be this lazy!)


Accordingly, let's consider a similar application that's a little more
challenging. How about testing all of my [clients'] web sites
dynamically (ie using similar code to the OP, rather than scanning logs,
etc)?

In this case, we could start with such entities/resources as the class
and a list containing the URLs.

We instantiate the class during the application's initialisation phase,
and the central loop runs 'down' the list.

Inside the loop, the .run() call passes the applicable-URL as an
argument to instance.run(), and thus the popen() is performed.

In this scenario the 'cost' of the class-init is spread across the
however-many URLs populating the list, and tends towards irrelevant.


In other applications, the post-processing might be considerably more
involved than the OP's needs. Which is why I might even (really make
@Alan grumble by) sub-class. Given that one application's
post-processing is likely quite different to another's, creating a
purpose-built sub-class enables both a customisation of the
post-processing, eg doing 'something' to a retrieved web-page or an
opened file; and keeping all this related functionality together.

In this thinking, keeping all the data used to construct the popen()
call, together with all the feedback, is not "internal state" per-se.
However, it is a means of keeping (like) things together. Thus the
rationale of "class" or "module" as a "namespace" is extended into the
same ideas as keeping all your fruit in a fruit-bowl; and all your pens,
pencils, etc, in a pencil-case/drawer/pen-holder/desk-organiser. NB this
'bowl of data and functionality' is not meant to be, and is by no means
an impenetrable barrier - we can still refer to instance.URL or
instance.BASH_command from the 'mainline' code.


My habit is to 'encapsulate' all I/O - which may mean using a
custom-function or a class (and yes dealing with subprocesses is
classified as a form of I/O for the purposes of this discussion). The
rationale for such is (a) modularity, and (b) that when it comes to
debugging/testing, substitution is easy - if you keeping banging-away on
Google's front door, or somebody's API, their IDS or other defences may
decide against you! (and you don't want that!); similarly, replacing
some long/expensive process with a 'mock'...


@Alan knows I'm a classy-guy, even though is yet to admit that I am
(also) better-looking - and we'll debate a point if only to illustrate
that there are often 'choices'...

So, although I claim to favor classes, there are many situations where a
function is the better choice, no argument. Sometimes a class offers an
advantage (or two). Possibly the same advantage might be obtained from
judicious use of modules...

It's definitely worth understanding each of the points made in this
thread's posts. The one that works for you today is the 'right' one -
and maybe a different choice will be 'right' tomorrow...


PS +1 on the major differences between Python 2.n and 3, and our
need/the relevance of being told such constraints!
-- 
Regards,
=dn


More information about the Tutor mailing list