1
# This file is part of DEAP.
3
# DEAP is free software: you can redistribute it and/or modify
4
# it under the terms of the GNU Lesser General Public License as
5
# published by the Free Software Foundation, either version 3 of
6
# the License, or (at your option) any later version.
8
# DEAP is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU Lesser General Public License for more details.
13
# You should have received a copy of the GNU Lesser General Public
14
# License along with DEAP. If not, see <http://www.gnu.org/licenses/>.
16
"""The :mod:`~deap.creator` module is the heart and soul of DEAP, it allows to
17
create, at runtime, classes that will fulfill the needs of your evolutionary
18
algorithms. This module follows the meta-factory paradigm by allowing to
19
create new classes via both composition and inheritance. Attributes both datas
20
and functions are added to existing types in order to create new types
21
empowered with user specific evolutionary computation capabilities. In effect,
22
new classes can be built from any imaginable type, from :class:`list` to
23
:class:`set`, :class:`dict`, :class:`~deap.base.Tree` and more,
24
providing the possibility to implement genetic algorithms, genetic
25
programming, evolution strategies, particle swarm optimizers, and many more.
32
"""Some classes in Python's standard library as well as third party library
33
may be in part incompatible with the logic used in DEAP. In order to palliate
34
to this problem, the method :func:`create` uses the dictionary
35
`class_replacers` to identify if the base type provided is problematic, and if
36
so the new class inherits from the replacement class instead of the
39
`class_replacers` keys are classes to be replaced and the values are the
45
(numpy.ndarray, numpy.array)
47
# Numpy is not present, skip the definition of the replacement class.
49
except AttributeError:
50
# Numpy is present, but there is either no ndarray or array in numpy,
51
# also skip the definition of the replacement class.
54
class _numpy_array(numpy.ndarray):
55
def __getslice__(self, i, j):
56
"""Overrides the getslice from numpy.ndarray that returns a shallow
59
return numpy.ndarray.__getslice__(self, i, j).copy()
61
def __deepcopy__(self, memo):
62
"""Overrides the deepcopy from numpy.ndarray that does not copy
63
the object's attributes.
65
copy_ = numpy.ndarray.__deepcopy__(self, memo)
66
copy_.__dict__.update(copy.deepcopy(self.__dict__, memo))
70
def __new__(cls, iterable):
71
"""Creates a new instance of a numpy.ndarray from a function call"""
72
return numpy.array(list(iterable)).view(cls)
74
def __array_finalize__(self, obj):
75
# __init__ will reinitialize every member of the subclass.
76
# this might not be desirable for example in the case of an ES.
79
# Instead, e could use the following that will simply deepcopy
80
# every member that is present in the original class
81
# This is significantly slower.
82
#if self.__class__ == obj.__class__:
83
# self.__dict__.update(copy.deepcopy(obj.__dict__))
84
class_replacers[numpy.ndarray] = _numpy_array
86
class _array(array.array):
88
def __new__(cls, seq=()):
89
return super(_array, cls).__new__(cls, cls.typecode, seq)
91
def __deepcopy__(self, memo):
92
"""Overrides the deepcopy from array.array that does not copy
93
the object's attributes and class type.
96
copy_ = cls.__new__(cls, self)
97
memo[id(self)] = copy_
98
copy_.__dict__.update(copy.deepcopy(self.__dict__, memo))
101
def __reduce__(self):
102
return (self.__class__, (list(self),), self.__dict__)
103
class_replacers[array.array] = _array
105
def create(name, base, **kargs):
106
"""Creates a new class named *name* inheriting from *base* in the
107
:mod:`~deap.creator` module. The new class can have attributes defined by
108
the subsequent keyword arguments passed to the function create. If the
109
argument is a class (without the parenthesis), the __init__ function is
110
called in the initialization of an instance of the new object and the
111
returned instance is added as an attribute of the class' instance.
112
Otherwise, if the argument is not a class, (for example an :class:`int`),
113
it is added as a "static" attribute of the class.
115
The following is used to create a class :class:`Foo` inheriting from the
116
standard :class:`list` and having an attribute :attr:`bar` being an empty
117
dictionary and a static attribute :attr:`spam` initialized to 1. ::
119
create("Foo", list, bar=dict, spam=1)
121
This above line is exactly the same as defining in the :mod:`creator`
122
module something like the following. ::
132
for obj_name, obj in kargs.iteritems():
133
if hasattr(obj, "__call__"):
134
dict_inst[obj_name] = obj
136
dict_cls[obj_name] = obj
138
# Check if the base class has to be replaced
139
if base in class_replacers:
140
base = class_replacers[base]
142
# A DeprecationWarning is raised when the object inherits from the
143
# class "object" which leave the option of passing arguments, but
144
# raise a warning stating that it will eventually stop permitting
145
# this option. Usually this happens when the base class does not
146
# override the __init__ method from object.
147
def initType(self, *args, **kargs):
148
"""Replace the __init__ function of the new type, in order to
149
add attributes that were defined with **kargs to the instance.
151
for obj_name, obj in dict_inst.iteritems():
152
setattr(self, obj_name, obj())
153
if base.__init__ is not object.__init__:
154
base.__init__(self, *args, **kargs)
158
objtype = type(name, (base,), dict_cls)
159
objtype.__init__ = initType
160
globals()[name] = objtype