I'm creating some classes for dealing with filenames in various types of file shares (nfs, afp, s3, local disk) etc. I get as user input a string that identifies the data source (i.e. "nfs://192.168.1.3"
or "s3://mybucket/data"
) etc.
I'm subclassing the specific filesystems from a base class that has common code. Where I'm confused is in the object creation. What I have is the following:
import os
class FileSystem(object):
class NoAccess(Exception):
pass
def __new__(cls,path):
if cls is FileSystem:
if path.upper().startswith('NFS://'):
return super(FileSystem,cls).__new__(Nfs)
else:
return super(FileSystem,cls).__new__(LocalDrive)
else:
return super(FileSystem,cls).__new__(cls,path)
def count_files(self):
raise NotImplementedError
class Nfs(FileSystem):
def __init__ (self,path):
pass
def count_files(self):
pass
class LocalDrive(FileSystem):
def __init__(self,path):
if not os.access(path, os.R_OK):
raise FileSystem.NoAccess('Cannot read directory')
self.path = path
def count_files(self):
return len([x for x in os.listdir(self.path) if os.path.isfile(os.path.join(self.path, x))])
data1 = FileSystem('nfs://192.168.1.18')
data2 = FileSystem('/var/log')
print type(data1)
print type(data2)
print data2.count_files()
I thought this would be a good use of __new__
but most posts I read about it's use discourage it. Is there a more accepted way to approach this problem?
In the base class object , the __new__ method is defined as a static method which requires to pass a parameter cls . cls represents the class that is needed to be instantiated, and the compiler automatically provides this parameter at the time of instantiation.
The __new__() is a static method of the object class. When you create a new object by calling the class, Python calls the __new__() method to create the object first and then calls the __init__() method to initialize the object's attributes.
The constructor function in python is called __new__ and __init__ is the initializer function. Quoting the python documentation, __new__ is used when you need to control the creation of a new instance while __init__ is used when you need to control the initialization of a new instance.
Things to remember So, __init__ will called implicitly. If __new__ method return something else other than instance of class, then instances __init__ method will not be invoked. In this case you have to call __init__ method yourself.
In my opinion, using __new__
in such a way is really confusing for other people who might read your code. Also it requires somewhat hackish code to distinguish guessing file system from user input and creating Nfs
and LocalDrive
with their corresponding classes.
Why not make a separate function with this behaviour? It can even be a static method of FileSystem
class:
class FileSystem(object):
# other code ...
@staticmethod
def from_path(path):
if path.upper().startswith('NFS://'):
return Nfs(path)
else:
return LocalDrive(path)
And you call it like this:
data1 = FileSystem.from_path('nfs://192.168.1.18')
data2 = FileSystem.from_path('/var/log')
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With