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?

\$\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.)

\$\endgroup\$

Your Answer

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