This article is the 8th day article of Python Advent Calendar 2015.
I usually write Go, and honestly I rarely write Python. I'm thinking about using it in mathematics, so I jumped into Python's Advent Calendar this time.
Now, in Go, it is important to define an interface and implement it in an abstract pattern for large-scale development. This isn't just about Go, objects are abstracted, and it's a great code that's more readable and less dependent on modest use.
Meanwhile, I investigated what kind of method is available for abstraction in Python. Use the one that does not exist in the language specification and is provided as a module named ABC (Abstract Base Class).
In Python, abstract classes can be implemented using the ABC (Abstract Base Class) (https://docs.python.org/3.5/library/abc.html) module. The abstract base class can be defined by a metaclass called ABCMeta, and the defined abstract base class can be used as a superclass to define subclasses.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from abc import ABCMeta, abstractmethod
#Abstract class
class Animal(metaclass=ABCMeta):
@abstractmethod
def sound(self):
pass
#Inherit abstract class
class Cat(Animal):
def sound(self):
print("Meow")
if __name__ == "__main__":
assert issubclass(Cat().__class__, Animal)
assert isinstance(Cat(), Animal)
We have defined an abstract class called ʻAnimal and implemented the
Cat class that inherited it. Since it is inherited, it seems natural that ʻis subclass
, ʻis instance` passes.
If the inherited Cat
class does not implement the sound
of the abstract method (@abstractmethod will be described later), a runtime error will occur as shown below. (* When creating an instance)
class Cat(Animal):
pass
# TypeError: Can't instantiate abstract class Cat with abstract methods sound
register
methodOn the other hand, instead of defining subclasses, it is possible to register unrelated classes to behave like abstract classes. This is called a ** virtual subclass **.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from abc import ABCMeta, abstractmethod
class Animal(metaclass=ABCMeta):
@abstractmethod
def sound(self):
pass
#Does not inherit the abstract class, but`sound`Implement method
class Dog():
def sound(self):
print("Bow")
#Register Dog to Animal of abstract class
Animal.register(Dog)
if __name__ == "__main__":
assert issubclass(Dog().__class__, Animal)
assert isinstance(Dog(), Animal)
If you register to a virtual subclass, that class will be an abstract class, but if the abstract method is not implemented, a runtime error will occur as shown below. (* When calling a method)
class Dog():
pass
# AttributeError: 'Dog' object has no attribute 'sound'
@abstractmethod
A decorator that represents an abstract method. Although it is an abstract method, it is also possible to describe the process in a method that specifies a decorator and call it from a subclass.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from abc import ABCMeta, abstractmethod
class Animal(metaclass=ABCMeta):
@abstractmethod
def sound(self):
print("Hello")
#Inherit abstract class
class Cat(Animal):
def sound(self):
#Call the inherited sound
super(Cat, self).sound()
print("Meow")
if __name__ == "__main__":
print(Cat().sound())
You can call the inherited abstract method with super (Cat, self) .sound ()
. It's a little different from Java.
@abstractclassmethod
(version 3.2)
It is a decorator of abstract class method, but from version 3.3, it is described by @ classmethod
as follows.
class Animal(metaclass=ABCMeta):
@classmethod
@abstractmethod
def sound_classmethod(self):
pass
@abstractstaticmethod
(version 3.2)
It is a decorator of abstract static method, but from version 3.3, it is described by @staticmethod
as follows.
class Animal(metaclass=ABCMeta):
@staticmethod
@abstractmethod
def sound_staticmethod(self):
pass
The above is enough for abstract classes, but I wanted to check [duck typing](https://ja.wikipedia.org/wiki/duck typing) after doing so, so I will implement it. ..
"If it walks like a duck and quacks like a duck, it must be a duck."-"If you walk like a duck and cry, it's a duck."
It's kind of like a name.
Let's simplify this and say, "If you cry, it's an animal." And if you forcibly put it into programming, "If you implement a method called" crying "on an object, the concrete class will be" animal "." It's hard to understand for the first time.
It's faster to see than to think in your head.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from abc import ABCMeta, abstractmethod
class Animal(metaclass=ABCMeta):
@abstractmethod
def sound(self):
pass
class Cat(Animal):
def sound(self):
print("Meow")
class Dog():
def sound(self):
print("Bow")
class Book():
pass
Animal.register(Dog)
def output(animal):
print(animal.__class__.__name__, end=": ")
animal.sound()
if __name__ == "__main__":
c = Cat()
output(c)
d = Dog()
output(d)
b = Book()
output(b)
If you implement and execute like, you will get the following result and runtime error.
Cat: Meow
Dog: Bow
AttributeError: 'Book' object has no attribute 'sound'
Book
without sound
in duck typing is OK because it is not "no sound = animal". However, I would like to detect the error if possible.
I can't deny the feeling that this is dynamically typed, but I'd like to avoid it if possible, so I'll do the following.
try
~ ʻexcept`def output(animal):
print(animal.__class__.__name__, end=": ")
try:
animal.sound()
except AttributeError:
print('No sound')
It is a method to catch if exception is thrown with try
, but since it is an execution base, it can be handled only after it actually becomes an exception. I can't recommend it very much.
hasattr
def output(animal):
if not hasattr(animal, 'sound'):
return
print(animal.__class__.__name__, end=": ")
animal.sound()
Checks if the Attribute exists. Well, it's reasonable.
def output(animal):
if not isinstance(animal, Animal):
return
print(animal.__class__.__name__, end=": ")
animal.sound()
Determines if the class is specified by ʻis instance`. This will allow you to handle the intended abstract class.
There was also a static typing check called mypy. If you're used to static typing, you can rest assured.
From Python...
def fib(n):
a, b = 0, 1
while a < n:
yield a
a, b = b, a+b
...to statically typed Python
def fib(n: int) -> Iterator[int]:
a, b = 0, 1
while a < n:
yield a
a, b = b, a+b
However, dynamically typed languages have established a position with it, so it seems subtle to make it static now.
It's nice to be able to implement abstract classes in Python, but I can't say when the error will occur. As for abstraction, it is interesting because the specifications are fixed in each language but the concept is slightly different. I feel that I feel the characteristics of the language.
Recommended Posts