4 ms·
I don't understand why "reader" is a bad name. One of the links says: > Reader/Writer – The main problem I have with these types of objects is that it only des
by dalke 12y ago
I don't understand why "reader" is a bad name. One of the links says:
> Reader/Writer – The main problem I have with these types of objects is that it only describes a small portion of the interaction that an object can have. A PhotoWriter is not an object, rather a Photo as an object has functionality binded to it so that it can be read or written.
I don't get that though.
Here's an example reader for the /etc/passwd file on my system. The reader iterates over PasswordEntry records, and the reader exposes a "Location" interface to get access to reader state, like the line number, which isn't logically part of the PasswordEntry. The reader also implements the context-manager and iterator APIs:
from collections import namedtuple
PasswordEntry = namedtuple("PasswordEntry",
"name password uid gid gecos home_dir shell")
class Location(object):
def __init__(self, filename, lineno=0, recno=0):
self.filename, self.lineno, self.recno = filename, lineno, recno
class PasswordReader(object):
def __init__(self, infile, reader_iter, location):
self._infile = infile
self._reader_iter = reader_iter
self.location = location
def __iter__(self):
return self._reader_iter
def __next__(self): # Python 3
return next(self._reader_iter)
def next(self): # Python 2
return next(self._reader_iter)
def close(self):
self._infile.close()
def __enter__(self):
return self
def __exit__(self, *args):
self.close()
def _read_password_entries(infile, location):
recno = 0
for lineno, line in enumerate(infile, 1):
location.lineno = lineno
if line.startswith("#"):
continue
fields = line.rstrip().split(":")
location.recno = recno = recno + 1
yield PasswordEntry(*fields)
def read_password_entries(filename):
infile = open(filename)
location = Location(filename)
reader_iter = _read_password_entries(infile, location)
return PasswordReader(infile, _read_password_entries(infile, location), location)
# Example code using the API
with read_password_entries("/etc/passwd") as reader:
for entry in reader:
if entry.shell != "/usr/bin/false":
print("Entry %r (record #%d, line %d) uses shell %r" % (
entry.name, reader.location.recno, reader.location.lineno, entry.shell))
## output for my system
# Entry 'root' (record #2, line 12) uses shell '/bin/sh'
# Entry '_uucp' (record #4, line 14) uses shell '/usr/sbin/uucico'
What's a better name than "PasswordReader"? (Other than PasswordEntryReader; I mean something which doesn't end in "-er".)
I don't understand the objection "it only describes a small portion of the interaction that an object can have". This is a reader, no? It reads entries, and that's pretty much all the interaction it has.
> They are not classes, and the objects they instantiate are not objects. Instead, they are collections of procedures pretending to be classes.
I also didn't understand this. How is the PasswordReader "pretending to be a class". It is a class, and methods like the context manager methods have to be implemented through a class, and not a "collection of procedures."
(Of course, C++ as "C with classes" is an example of OO programming as data structures plus a collection of procedures, where the language makes it easier to tie the two together. But that can't be the objection as then all C++ OO code is "pretending to be a class", making this an argument from extreme purism rather than one with more practical implications.)
- yegor256a 12y agoHow about "PasswordEntries" instead?
- dalke 12y agoThat's more often used for a collection with random-access lookup, like a list of PasswordEntry instances. A specialized class might be indexed for lookups, as in: class PasswordEntries(object): def __init__(self, entries): self.entries = entries self._by_name = {entry.name: entry for entry in entries} # Also add lookup by uid, gid, etc. def __len__(self): return len(self.entries) def __getitem__(self, i): return self.entries[i] def get_name(self, name): return self._by_name.get(name) # other public lookup methods here entries = PasswordEntries(read_password_entries("/etc/passwd")) print "root uses", entries.get_name("root").shell print "_lp uses", entries.get_name("_lp").shell It's a bad name for a reader, which is more closely tied with I/O. The suffix "-reader" suggests support for file operations (like 'close()'), use as a context manager, and awareness that the values will only be available once. The suffix "-s" does not. At the very least it suggests that it's possible to start a new iteration over the complete container of entries. While you work to come up with a better name than "PasswdReader", what is your replacement name for "generator", as in: >>> def f(): ... yield 1 ... >>> x = f() >>> x <generator object f at 0x103ac3f50> >>> x.__class__ <type 'generator'> Your essay places the -or names into the -er category (they are, after all, indicators of action nouns), so why is "generator" a bad name?
- yegor256a 12y agoThe idea of object-oriented programming is that you, a client of that reader, should NOT know that it reads from I/O. You should not rely on this information. You get a list of entries. Where they are coming from, either I/O, memory or a user console, is none of your business. It's an internal information of the object that gives them to you.
- dalke 12y ago