Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

""" 

Fast methods via Cython 

  

This module provides extension classes with useful methods of cython speed, 

that python classes can inherit. 

  

.. NOTE:: 

  

This module provides a cython base class :class:`WithEqualityById` 

implementing unique instance behaviour, and a cython base class 

:class:`FastHashable_class`, which has a quite fast hash 

whose value can be freely chosen at initialisation time. 

  

AUTHOR: 

  

- Simon King (2013-02): Original version 

- Simon King (2013-10): Add :class:`Singleton` 

  

""" 

  

#***************************************************************************** 

# Copyright (C) 2013 Simon A. King <simon.king at uni-jena.de> 

# 

# This program is free software: you can redistribute it and/or modify 

# it under the terms of the GNU General Public License as published by 

# the Free Software Foundation, either version 2 of the License, or 

# (at your option) any later version. 

# http://www.gnu.org/licenses/ 

#***************************************************************************** 

  

from __future__ import absolute_import, print_function 

  

from sage.misc.classcall_metaclass import ClasscallMetaclass, typecall 

from sage.misc.constant_function import ConstantFunction 

  

from cpython.object cimport Py_EQ, Py_NE 

  

  

cdef class WithEqualityById: 

""" 

Provide hash and equality test based on identity. 

  

.. NOTE:: 

  

This class provides the unique representation behaviour of 

:class:`~sage.structure.unique_representation.UniqueRepresentation`, 

together with :class:`~sage.structure.unique_representation.CachedRepresentation`. 

  

EXAMPLES: 

  

Any instance of :class:`~sage.structure.unique_representation.UniqueRepresentation` 

inherits from :class:`WithEqualityById`. 

:: 

  

sage: class MyParent(Parent): 

....: def __init__(self, x): 

....: self.x = x 

....: def __hash__(self): 

....: return hash(self.x) 

sage: class MyUniqueParent(UniqueRepresentation, MyParent): pass 

sage: issubclass(MyUniqueParent, sage.misc.fast_methods.WithEqualityById) 

True 

  

Inheriting from :class:`WithEqualityById` provides unique representation 

behaviour:: 

  

sage: a = MyUniqueParent(1) 

sage: b = MyUniqueParent(2) 

sage: c = MyUniqueParent(1) 

sage: a is c 

True 

sage: d = MyUniqueParent(-1) 

sage: a == d 

False 

  

The hash inherited from ``MyParent`` is replaced by a hash that coincides 

with :class:`object`'s hash:: 

  

sage: hash(a) == hash(a.x) 

False 

sage: hash(a) == object.__hash__(a) 

True 

  

.. WARNING:: 

  

It is possible to inherit from 

:class:`~sage.structure.unique_representation.UniqueRepresentation` 

and then overload equality test in a way that destroys the unique 

representation property. We strongly recommend against it! You should 

use :class:`~sage.structure.unique_representation.CachedRepresentation` 

instead. 

  

:: 

  

sage: class MyNonUniqueParent(MyUniqueParent): 

....: def __eq__(self, other): 

....: return self.x^2 == other.x^2 

sage: a = MyNonUniqueParent(1) 

sage: d = MyNonUniqueParent(-1) 

sage: a is MyNonUniqueParent(1) 

True 

sage: a == d 

True 

sage: a is d 

False 

  

""" 

def __hash__(self): 

""" 

The hash provided by this class coincides with that of ``<type 'object'>``. 

  

TESTS:: 

  

sage: class MyParent(Parent): 

....: def __init__(self, x): 

....: self.x = x 

....: def __hash__(self): 

....: return hash(self.x) 

sage: class MyUniqueParent(UniqueRepresentation, MyParent): pass 

sage: issubclass(MyUniqueParent, sage.misc.fast_methods.WithEqualityById) 

True 

sage: a = MyUniqueParent(1) 

sage: hash(a) == hash(a.x) 

False 

sage: hash(a) == object.__hash__(a) 

True 

  

sage: from sage.misc.fast_methods import WithEqualityById 

sage: o1 = WithEqualityById() 

sage: o2 = WithEqualityById() 

sage: hash(o1) == hash(o2) 

False 

""" 

# This is the default hash function in Python's object.c: 

return hash_by_id(<void *>self) 

  

def __richcmp__(self, other, int op): 

""" 

Equality test provided by this class is by identity. 

  

TESTS:: 

  

sage: class MyParent(Parent): 

....: def __init__(self, x): 

....: self.x = x 

....: def __hash__(self): 

....: return hash(self.x) 

sage: class MyUniqueParent(UniqueRepresentation, MyParent): pass 

sage: issubclass(MyUniqueParent, sage.misc.fast_methods.WithEqualityById) 

True 

sage: a = MyUniqueParent(1) 

sage: b = MyUniqueParent(-1) 

  

Equality test takes into account identity:: 

  

sage: a == b 

False 

  

When comparing with an object which is not an instance of 

``WithEqualityById``, the other object determines the 

comparison:: 

  

sage: class AlwaysEqual: 

....: def __eq__(self, other): 

....: return True 

sage: AlwaysEqual() == a 

True 

sage: a == AlwaysEqual() 

True 

  

Check that :trac:`19628` is fixed:: 

  

sage: from sage.misc.lazy_import import LazyImport 

sage: lazyQQ = LazyImport('sage.all', 'QQ') 

sage: PolynomialRing(lazyQQ, 'ijk') is PolynomialRing(QQ, 'ijk') 

True 

sage: PolynomialRing(QQ, 'ijkl') is PolynomialRing(lazyQQ, 'ijkl') 

True 

""" 

# This only makes sense if "other" is also of type WithEqualityById 

if type(self) is not type(other): 

if not isinstance(other, WithEqualityById): 

return NotImplemented 

  

if op == Py_EQ: 

return self is other 

elif op == Py_NE: 

return self is not other 

return NotImplemented 

  

  

cdef class FastHashable_class: 

""" 

A class that has a fast hash method, returning a pre-assigned value. 

  

.. NOTE:: 

  

This is for internal use only. The class has a cdef attribute 

``_hash``, that needs to be assigned (for example, by calling 

the init method, or by a direct assignement using 

cython). This is slower than using :func:`provide_hash_by_id`, 

but has the advantage that the hash can be prescribed, by 

assigning a cdef attribute ``_hash``. 

  

TESTS:: 

  

sage: from sage.misc.fast_methods import FastHashable_class 

sage: H = FastHashable_class(123) 

sage: hash(H) 

123 

""" 

def __init__(self, h): 

""" 

TESTS:: 

  

sage: from sage.misc.fast_methods import FastHashable_class 

sage: H = FastHashable_class(123) 

sage: hash(H) # indirect doctest 

123 

""" 

self._hash = h 

  

def __hash__(self): 

""" 

TESTS:: 

  

sage: from sage.misc.fast_methods import FastHashable_class 

sage: H = FastHashable_class(123) 

sage: hash(H) # indirect doctest 

123 

  

""" 

return self._hash 

  

  

class Singleton(WithEqualityById, metaclass=ClasscallMetaclass): 

""" 

A base class for singletons. 

  

A singleton is a class that allows to create not more than a 

single instance. This instance can also belong to a subclass, but 

it is not possible to have several subclasses of a singleton all 

having distinct unique instances. 

  

In order to create a singleton, just add :class:`Singleton` 

to the list of base classes:: 

  

sage: from sage.misc.fast_methods import Singleton 

sage: class C(Singleton, SageObject): 

....: def __init__(self): 

....: print("creating singleton") 

sage: c = C() 

creating singleton 

sage: c2 = C() 

sage: c is c2 

True 

  

The unique instance of a singleton stays in memory as long as the 

singleton itself does. 

  

Pickling, copying, hashing, and comparison are provided for by 

:class:`Singleton` according to the singleton paradigm. Note 

that pickling fails if the class is replaced by a sub-sub-class 

after creation of the instance:: 

  

sage: class D(C): 

....: pass 

sage: import __main__ # This is only needed ... 

sage: __main__.C = C # ... in doctests 

sage: __main__.D = D # same here, only in doctests 

sage: orig = type(c) 

sage: c.__class__ = D 

sage: orig == type(c) 

False 

sage: loads(dumps(c)) 

Traceback (most recent call last): 

... 

AssertionError: <class '__main__.D'> is not a direct subclass of <class 'sage.misc.fast_methods.Singleton'> 

""" 

@staticmethod 

def __classcall__(cls): 

""" 

Create an instance ``O`` of the given class ``cls``, and make it 

so that in future both ``cls.__call__`` and ``O.__class__.__call__`` 

are constant functions returning ``O``. 

  

EXAMPLES:: 

  

sage: from sage.misc.fast_methods import Singleton 

sage: class C(Singleton, Parent): 

....: def __init__(self): 

....: print("creating singleton") 

....: Parent.__init__(self, base=ZZ, category=Rings()) 

sage: c = C() 

creating singleton 

sage: import __main__ # This is only needed ... 

sage: __main__.C = C # ... in doctests 

sage: loads(dumps(c)) is copy(c) is C() # indirect doctest 

True 

""" 

assert cls.mro()[1] == Singleton, "{} is not a direct subclass of {}".format(cls, Singleton) 

res = typecall(cls) 

cf = ConstantFunction(res) 

cls._set_classcall(cf) 

res.__class__._set_classcall(cf) 

return res 

  

def __copy__(self): 

""" 

There is a unique instance of a singleton, hence, copying 

returns ``self``. 

  

EXAMPLES:: 

  

sage: from sage.misc.fast_methods import Singleton 

sage: class C(Singleton, Parent):  

....: def __init__(self): 

....: print("creating singleton") 

....: Parent.__init__(self, base=ZZ, category=Rings()) 

sage: c = C() 

creating singleton 

sage: import __main__ # This is only needed ... 

sage: __main__.C = C # ... in doctests 

sage: loads(dumps(c)) is copy(c) is C() # indirect doctest 

True 

"""  

return self 

  

def __reduce__(self): 

""" 

There is a unique instance of a singleton, hence, pickling 

returns ``self``. 

  

EXAMPLES:: 

  

sage: from sage.misc.fast_methods import Singleton 

sage: class C(Singleton, Parent):  

....: def __init__(self): 

....: print("creating singleton") 

....: Parent.__init__(self, base=ZZ, category=Rings()) 

....: 

sage: c = C() 

creating singleton 

sage: import __main__ # This is only needed ... 

sage: __main__.C = C # ... in doctests 

sage: loads(dumps(c)) is copy(c) is C() # indirect doctest 

True 

 

The pickle data mainly consist of the class of the unique instance, 

which may be a subclass of the original class used to create the 

instance.If the class is replaced by a sub-sub-class after creation 

of the instance, pickling fails. See the doctest 

in :class:`Singleton`. 

"""  

return self.__class__, ()