singleton.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. """Singleton mechanism"""
  2. from .core import Registry
  3. from .sympify import sympify
  4. class SingletonRegistry(Registry):
  5. """
  6. The registry for the singleton classes (accessible as ``S``).
  7. Explanation
  8. ===========
  9. This class serves as two separate things.
  10. The first thing it is is the ``SingletonRegistry``. Several classes in
  11. SymPy appear so often that they are singletonized, that is, using some
  12. metaprogramming they are made so that they can only be instantiated once
  13. (see the :class:`sympy.core.singleton.Singleton` class for details). For
  14. instance, every time you create ``Integer(0)``, this will return the same
  15. instance, :class:`sympy.core.numbers.Zero`. All singleton instances are
  16. attributes of the ``S`` object, so ``Integer(0)`` can also be accessed as
  17. ``S.Zero``.
  18. Singletonization offers two advantages: it saves memory, and it allows
  19. fast comparison. It saves memory because no matter how many times the
  20. singletonized objects appear in expressions in memory, they all point to
  21. the same single instance in memory. The fast comparison comes from the
  22. fact that you can use ``is`` to compare exact instances in Python
  23. (usually, you need to use ``==`` to compare things). ``is`` compares
  24. objects by memory address, and is very fast.
  25. Examples
  26. ========
  27. >>> from sympy import S, Integer
  28. >>> a = Integer(0)
  29. >>> a is S.Zero
  30. True
  31. For the most part, the fact that certain objects are singletonized is an
  32. implementation detail that users should not need to worry about. In SymPy
  33. library code, ``is`` comparison is often used for performance purposes
  34. The primary advantage of ``S`` for end users is the convenient access to
  35. certain instances that are otherwise difficult to type, like ``S.Half``
  36. (instead of ``Rational(1, 2)``).
  37. When using ``is`` comparison, make sure the argument is sympified. For
  38. instance,
  39. >>> x = 0
  40. >>> x is S.Zero
  41. False
  42. This problem is not an issue when using ``==``, which is recommended for
  43. most use-cases:
  44. >>> 0 == S.Zero
  45. True
  46. The second thing ``S`` is is a shortcut for
  47. :func:`sympy.core.sympify.sympify`. :func:`sympy.core.sympify.sympify` is
  48. the function that converts Python objects such as ``int(1)`` into SymPy
  49. objects such as ``Integer(1)``. It also converts the string form of an
  50. expression into a SymPy expression, like ``sympify("x**2")`` ->
  51. ``Symbol("x")**2``. ``S(1)`` is the same thing as ``sympify(1)``
  52. (basically, ``S.__call__`` has been defined to call ``sympify``).
  53. This is for convenience, since ``S`` is a single letter. It's mostly
  54. useful for defining rational numbers. Consider an expression like ``x +
  55. 1/2``. If you enter this directly in Python, it will evaluate the ``1/2``
  56. and give ``0.5``, because both arguments are ints (see also
  57. :ref:`tutorial-gotchas-final-notes`). However, in SymPy, you usually want
  58. the quotient of two integers to give an exact rational number. The way
  59. Python's evaluation works, at least one side of an operator needs to be a
  60. SymPy object for the SymPy evaluation to take over. You could write this
  61. as ``x + Rational(1, 2)``, but this is a lot more typing. A shorter
  62. version is ``x + S(1)/2``. Since ``S(1)`` returns ``Integer(1)``, the
  63. division will return a ``Rational`` type, since it will call
  64. ``Integer.__truediv__``, which knows how to return a ``Rational``.
  65. """
  66. __slots__ = ()
  67. # Also allow things like S(5)
  68. __call__ = staticmethod(sympify)
  69. def __init__(self):
  70. self._classes_to_install = {}
  71. # Dict of classes that have been registered, but that have not have been
  72. # installed as an attribute of this SingletonRegistry.
  73. # Installation automatically happens at the first attempt to access the
  74. # attribute.
  75. # The purpose of this is to allow registration during class
  76. # initialization during import, but not trigger object creation until
  77. # actual use (which should not happen until after all imports are
  78. # finished).
  79. def register(self, cls):
  80. # Make sure a duplicate class overwrites the old one
  81. if hasattr(self, cls.__name__):
  82. delattr(self, cls.__name__)
  83. self._classes_to_install[cls.__name__] = cls
  84. def __getattr__(self, name):
  85. """Python calls __getattr__ if no attribute of that name was installed
  86. yet.
  87. Explanation
  88. ===========
  89. This __getattr__ checks whether a class with the requested name was
  90. already registered but not installed; if no, raises an AttributeError.
  91. Otherwise, retrieves the class, calculates its singleton value, installs
  92. it as an attribute of the given name, and unregisters the class."""
  93. if name not in self._classes_to_install:
  94. raise AttributeError(
  95. "Attribute '%s' was not installed on SymPy registry %s" % (
  96. name, self))
  97. class_to_install = self._classes_to_install[name]
  98. value_to_install = class_to_install()
  99. self.__setattr__(name, value_to_install)
  100. del self._classes_to_install[name]
  101. return value_to_install
  102. def __repr__(self):
  103. return "S"
  104. S = SingletonRegistry()
  105. class Singleton(type):
  106. """
  107. Metaclass for singleton classes.
  108. Explanation
  109. ===========
  110. A singleton class has only one instance which is returned every time the
  111. class is instantiated. Additionally, this instance can be accessed through
  112. the global registry object ``S`` as ``S.<class_name>``.
  113. Examples
  114. ========
  115. >>> from sympy import S, Basic
  116. >>> from sympy.core.singleton import Singleton
  117. >>> class MySingleton(Basic, metaclass=Singleton):
  118. ... pass
  119. >>> Basic() is Basic()
  120. False
  121. >>> MySingleton() is MySingleton()
  122. True
  123. >>> S.MySingleton is MySingleton()
  124. True
  125. Notes
  126. =====
  127. Instance creation is delayed until the first time the value is accessed.
  128. (SymPy versions before 1.0 would create the instance during class
  129. creation time, which would be prone to import cycles.)
  130. """
  131. def __init__(cls, *args, **kwargs):
  132. cls._instance = obj = Basic.__new__(cls)
  133. cls.__new__ = lambda cls: obj
  134. cls.__getnewargs__ = lambda obj: ()
  135. cls.__getstate__ = lambda obj: None
  136. S.register(cls)
  137. # Delayed to avoid cyclic import
  138. from .basic import Basic