1
\$\begingroup\$

I have the following class to help me deal with files:

File class:

# -*- coding: utf-8 -*-
"""
file
~~~~~~~~~~~~~~~~
"""
from mimetypes import MimeTypes
from os import path
from lazyproperty import lazyproperty
class File(object):
 """Class to help dealing with files.
 """
 def __init__(self, path):
 """The constructor function.
 """
 self.path = path
 @lazyproperty
 def basename(self):
 """Return last portion of the file.
 """
 return path.basename(self.path)
 @lazyproperty
 def extension(self):
 """The extension name of the file, from the last occurrence of the
 . (period) character to end of string.
 """
 remaining, extension = path.splitext(self.path)
 return extension
 @lazyproperty
 def encoding(self):
 """The encoding of the file, if no encoding is found, None will be
 the default.
 """
 return self.__mime_type_and_encoding[1]
 @lazyproperty
 def abspath(self):
 """The absolute path of the file.
 """
 return path.abspath(self.path)
 @lazyproperty
 def mime_type(self):
 """The mime type associated with the file, if no mime type is found,
 None will be the default.
 """
 return self.__mime_type_and_encoding[0]
 # @private
 @lazyproperty
 def __mime_type_and_encoding(self):
 """Guess the type of a file based on its filename or URL, given by url.
 The return value is a tuple (type, encoding) where type is None if the
 type can’t be guessed (missing or unknown suffix) or a string of the
 form 'type/subtype', usable for a MIME content-type header.
 """
 return MimeTypes().guess_type(self.abspath)

lazyloading class:

# -*- coding: utf-8 -*-
"""
lazyproperty
~~~~~~~~~~~~~~~~
"""
class lazyproperty(object):
 """Class to create lazy properties.
 """
 def __init__(self, func):
 """The constructor function.
 """
 self.func = func
 def __get__(self, instance, cls):
 if instance is None: return self
 value = self.func(instance)
 setattr(instance, self.func.__name__, value)
 return value

I would like to improve the lazyproperties mime_type and encoding, this two parts:

return self.__mime_type_and_encoding[1]
return self.__mime_type_and_encoding[0]

Are making my sad, this magic numbers are very bad for maintenance. What i'm doing:

I'm using python 2.5 because i'm working in this legacy code, and the only way to get the mime type of file in this version is using the MimeTypes class:

mime_type = MimeTypes().guess_type("path/to/file")

Returns a tuple, -> ("mime_type", "encoding")

Which doesn't make any sense, since i'm asking for the mime type, not the encoding... any way... so, i created the __mime_type_and_encoding lazyproperty to avoid calling the MimeTypes() two times, in the encoding and mime_type.

Is it possible to avoid the magic numbers, and still do what i want?

200_success
145k22 gold badges190 silver badges478 bronze badges
asked Jan 6, 2017 at 18:29
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$
  1. In Python it's conventional to use a single underscore to indicate that a method is only for internal use (see PEP8). The double underscore is intended to solve a particular problem, namely avoiding conflict with a method of the same name in another class, when the classes are combined via inheritance. Since you don't have to worry about that problem here, you don't need the double underscore.

  2. In Python 2.6 or later, a simple way to avoid magic numbers would be to make a collections.namedtuple:

    from collections import namedtuple
    MimeTypeAndEncoding = namedtuple('MimeTypeAndEncoding', 'type encoding')
    

    and then in _mime_type_and_encoding write:

    return MimeTypeAndEncoding(*MimeTypes().guess_type(self.abspath))
    

    and in mime_type:

    return self._mime_type_and_encoding.type
    

    But this won't work for you because you are stuck on 2.5. But you could do much the same thing (if a bit more long-windedly) using a class:

    class MimeTypeAndEncoding(object):
     def __init__(self, type, encoding):
     self.type = type
     self.encoding = encoding
    
  3. In Python 3.2 or later you could implement lazy_property like this, using functools.lru_cache:

    from functools import lru_cache
    def lazy_property(method):
     "Decorator for a property that caches the result of the first call."
     return property(lru_cache(1)(method))
    

    (This won't work for you because you're stuck on Python 2.5, but I think it's neat.)

answered Jan 14, 2017 at 23:05
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.