annoy

Форк
0
/
annoymodule.cc 
685 строк · 20.7 Кб
1
// Copyright (c) 2013 Spotify AB
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4
// use this file except in compliance with the License. You may obtain a copy of
5
// the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12
// License for the specific language governing permissions and limitations under
13
// the License.
14

15
#include "annoylib.h"
16
#include "kissrandom.h"
17
#include "Python.h"
18
#include "structmember.h"
19
#include <exception>
20
#if defined(_MSC_VER) && _MSC_VER == 1500
21
typedef signed __int32    int32_t;
22
#else
23
#include <stdint.h>
24
#endif
25

26

27
#if defined(ANNOYLIB_USE_AVX512)
28
#define AVX_INFO "Using 512-bit AVX instructions"
29
#elif defined(ANNOYLIB_USE_AVX128)
30
#define AVX_INFO "Using 128-bit AVX instructions"
31
#else
32
#define AVX_INFO "Not using AVX instructions"
33
#endif
34

35
#if defined(_MSC_VER)
36
#define COMPILER_INFO "Compiled using MSC"
37
#elif defined(__GNUC__)
38
#define COMPILER_INFO "Compiled on GCC"
39
#else
40
#define COMPILER_INFO "Compiled on unknown platform"
41
#endif
42

43
#define ANNOY_DOC (COMPILER_INFO ". " AVX_INFO ".")
44

45
#if PY_MAJOR_VERSION >= 3
46
#define IS_PY3K
47
#endif
48

49
#ifndef Py_TYPE
50
    #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
51
#endif
52

53
#ifdef IS_PY3K
54
    #define PyInt_FromLong PyLong_FromLong 
55
#endif
56

57
using namespace Annoy;
58

59
#ifdef ANNOYLIB_MULTITHREADED_BUILD
60
  typedef AnnoyIndexMultiThreadedBuildPolicy AnnoyIndexThreadedBuildPolicy;
61
#else
62
  typedef AnnoyIndexSingleThreadedBuildPolicy AnnoyIndexThreadedBuildPolicy;
63
#endif
64

65
template class Annoy::AnnoyIndexInterface<int32_t, float>;
66

67
class HammingWrapper : public AnnoyIndexInterface<int32_t, float> {
68
  // Wrapper class for Hamming distance, using composition.
69
  // This translates binary (float) vectors into packed uint64_t vectors.
70
  // This is questionable from a performance point of view. Should reconsider this solution.
71
private:
72
  int32_t _f_external, _f_internal;
73
  AnnoyIndex<int32_t, uint64_t, Hamming, Kiss64Random, AnnoyIndexThreadedBuildPolicy> _index;
74
  void _pack(const float* src, uint64_t* dst) const {
75
    for (int32_t i = 0; i < _f_internal; i++) {
76
      dst[i] = 0;
77
      for (int32_t j = 0; j < 64 && i*64+j < _f_external; j++) {
78
	dst[i] |= (uint64_t)(src[i * 64 + j] > 0.5) << j;
79
      }
80
    }
81
  };
82
  void _unpack(const uint64_t* src, float* dst) const {
83
    for (int32_t i = 0; i < _f_external; i++) {
84
      dst[i] = (src[i / 64] >> (i % 64)) & 1;
85
    }
86
  };
87
public:
88
  HammingWrapper(int f) : _f_external(f), _f_internal((f + 63) / 64), _index((f + 63) / 64) {};
89
  bool add_item(int32_t item, const float* w, char**error) {
90
    vector<uint64_t> w_internal(_f_internal, 0);
91
    _pack(w, &w_internal[0]);
92
    return _index.add_item(item, &w_internal[0], error);
93
  };
94
  bool build(int q, int n_threads, char** error) { return _index.build(q, n_threads, error); };
95
  bool unbuild(char** error) { return _index.unbuild(error); };
96
  bool save(const char* filename, bool prefault, char** error) { return _index.save(filename, prefault, error); };
97
  void unload() { _index.unload(); };
98
  bool load(const char* filename, bool prefault, char** error) { return _index.load(filename, prefault, error); };
99
  float get_distance(int32_t i, int32_t j) const { return _index.get_distance(i, j); };
100
  void get_nns_by_item(int32_t item, size_t n, int search_k, vector<int32_t>* result, vector<float>* distances) const {
101
    if (distances) {
102
      vector<uint64_t> distances_internal;
103
      _index.get_nns_by_item(item, n, search_k, result, &distances_internal);
104
      distances->insert(distances->begin(), distances_internal.begin(), distances_internal.end());
105
    } else {
106
      _index.get_nns_by_item(item, n, search_k, result, NULL);
107
    }
108
  };
109
  void get_nns_by_vector(const float* w, size_t n, int search_k, vector<int32_t>* result, vector<float>* distances) const {
110
    vector<uint64_t> w_internal(_f_internal, 0);
111
    _pack(w, &w_internal[0]);
112
    if (distances) {
113
      vector<uint64_t> distances_internal;
114
      _index.get_nns_by_vector(&w_internal[0], n, search_k, result, &distances_internal);
115
      distances->insert(distances->begin(), distances_internal.begin(), distances_internal.end());
116
    } else {
117
      _index.get_nns_by_vector(&w_internal[0], n, search_k, result, NULL);
118
    }
119
  };
120
  int32_t get_n_items() const { return _index.get_n_items(); };
121
  int32_t get_n_trees() const { return _index.get_n_trees(); };
122
  void verbose(bool v) { _index.verbose(v); };
123
  void get_item(int32_t item, float* v) const {
124
    vector<uint64_t> v_internal(_f_internal, 0);
125
    _index.get_item(item, &v_internal[0]);
126
    _unpack(&v_internal[0], v);
127
  };
128
  void set_seed(uint64_t q) { _index.set_seed(q); };
129
  bool on_disk_build(const char* filename, char** error) { return _index.on_disk_build(filename, error); };
130
};
131

132
// annoy python object
133
typedef struct {
134
  PyObject_HEAD
135
  int f;
136
  AnnoyIndexInterface<int32_t, float>* ptr;
137
} py_annoy;
138

139

140
static PyObject *
141
py_an_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) {
142
  py_annoy *self = (py_annoy *)type->tp_alloc(type, 0);
143
  if (self == NULL) {
144
    return NULL;
145
  }
146
  const char *metric = NULL;
147

148
  static char const * kwlist[] = {"f", "metric", NULL};
149
  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|s", (char**)kwlist, &self->f, &metric))
150
    return NULL;
151
  if (!metric) {
152
    // This keeps coming up, see #368 etc
153
    PyErr_WarnEx(PyExc_FutureWarning, "The default argument for metric will be removed "
154
		 "in future version of Annoy. Please pass metric='angular' explicitly.", 1);
155
    self->ptr = new AnnoyIndex<int32_t, float, Angular, Kiss64Random, AnnoyIndexThreadedBuildPolicy>(self->f);
156
  } else if (!strcmp(metric, "angular")) {
157
   self->ptr = new AnnoyIndex<int32_t, float, Angular, Kiss64Random, AnnoyIndexThreadedBuildPolicy>(self->f);
158
  } else if (!strcmp(metric, "euclidean")) {
159
    self->ptr = new AnnoyIndex<int32_t, float, Euclidean, Kiss64Random, AnnoyIndexThreadedBuildPolicy>(self->f);
160
  } else if (!strcmp(metric, "manhattan")) {
161
    self->ptr = new AnnoyIndex<int32_t, float, Manhattan, Kiss64Random, AnnoyIndexThreadedBuildPolicy>(self->f);
162
  } else if (!strcmp(metric, "hamming")) {
163
    self->ptr = new HammingWrapper(self->f);
164
  } else if (!strcmp(metric, "dot")) {
165
    self->ptr = new AnnoyIndex<int32_t, float, DotProduct, Kiss64Random, AnnoyIndexThreadedBuildPolicy>(self->f);
166
  } else {
167
    PyErr_SetString(PyExc_ValueError, "No such metric");
168
    return NULL;
169
  }
170

171
  return (PyObject *)self;
172
}
173

174

175
static int 
176
py_an_init(py_annoy *self, PyObject *args, PyObject *kwargs) {
177
  // Seems to be needed for Python 3
178
  const char *metric = NULL;
179
  int f;
180
  static char const * kwlist[] = {"f", "metric", NULL};
181
  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|s", (char**)kwlist, &f, &metric))
182
    return (int) NULL;
183
  return 0;
184
}
185

186

187
static void 
188
py_an_dealloc(py_annoy* self) {
189
  delete self->ptr;
190
  Py_TYPE(self)->tp_free((PyObject*)self);
191
}
192

193

194
static PyMemberDef py_annoy_members[] = {
195
  {(char*)"f", T_INT, offsetof(py_annoy, f), 0,
196
   (char*)""},
197
  {NULL}	/* Sentinel */
198
};
199

200

201
static PyObject *
202
py_an_load(py_annoy *self, PyObject *args, PyObject *kwargs) {
203
  char *filename, *error;
204
  bool prefault = false;
205
  if (!self->ptr) 
206
    return NULL;
207
  static char const * kwlist[] = {"fn", "prefault", NULL};
208
  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|b", (char**)kwlist, &filename, &prefault))
209
    return NULL;
210

211
  if (!self->ptr->load(filename, prefault, &error)) {
212
    PyErr_SetString(PyExc_IOError, error);
213
    free(error);
214
    return NULL;
215
  }
216
  Py_RETURN_TRUE;
217
}
218

219

220
static PyObject *
221
py_an_save(py_annoy *self, PyObject *args, PyObject *kwargs) {
222
  char *filename, *error;
223
  bool prefault = false;
224
  if (!self->ptr) 
225
    return NULL;
226
  static char const * kwlist[] = {"fn", "prefault", NULL};
227
  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|b", (char**)kwlist, &filename, &prefault))
228
    return NULL;
229

230
  if (!self->ptr->save(filename, prefault, &error)) {
231
    PyErr_SetString(PyExc_IOError, error);
232
    free(error);
233
    return NULL;
234
  }
235
  Py_RETURN_TRUE;
236
}
237

238

239
PyObject*
240
get_nns_to_python(const vector<int32_t>& result, const vector<float>& distances, int include_distances) {
241
  PyObject* l = NULL;
242
  PyObject* d = NULL;
243
  PyObject* t = NULL;
244

245
  if ((l = PyList_New(result.size())) == NULL) {
246
    goto error;
247
  }
248
  for (size_t i = 0; i < result.size(); i++) {
249
    PyObject* res = PyInt_FromLong(result[i]);
250
    if (res == NULL) {
251
      goto error;
252
    }
253
    PyList_SetItem(l, i, res);
254
  }
255
  if (!include_distances)
256
    return l;
257

258
  if ((d = PyList_New(distances.size())) == NULL) {
259
    goto error;
260
  }
261

262
  for (size_t i = 0; i < distances.size(); i++) {
263
    PyObject* dist = PyFloat_FromDouble(distances[i]);
264
    if (dist == NULL) {
265
      goto error;
266
    }
267
    PyList_SetItem(d, i, dist);
268
  }
269

270
  if ((t = PyTuple_Pack(2, l, d)) == NULL) {
271
    goto error;
272
  }
273
  Py_XDECREF(l);
274
  Py_XDECREF(d);
275

276
  return t;
277

278
  error:
279
    Py_XDECREF(l);
280
    Py_XDECREF(d);
281
    Py_XDECREF(t);
282
    return NULL;
283
}
284

285

286
bool check_constraints(py_annoy *self, int32_t item, bool building) {
287
  if (item < 0) {
288
    PyErr_SetString(PyExc_IndexError, "Item index can not be negative");
289
    return false;
290
  } else if (!building && item >= self->ptr->get_n_items()) {
291
    PyErr_SetString(PyExc_IndexError, "Item index larger than the largest item index");
292
    return false;
293
  } else {
294
    return true;
295
  }
296
}
297

298
static PyObject* 
299
py_an_get_nns_by_item(py_annoy *self, PyObject *args, PyObject *kwargs) {
300
  int32_t item, n, search_k=-1, include_distances=0;
301
  if (!self->ptr) 
302
    return NULL;
303

304
  static char const * kwlist[] = {"i", "n", "search_k", "include_distances", NULL};
305
  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii|ii", (char**)kwlist, &item, &n, &search_k, &include_distances))
306
    return NULL;
307

308
  if (!check_constraints(self, item, false)) {
309
    return NULL;
310
  }
311

312
  vector<int32_t> result;
313
  vector<float> distances;
314

315
  Py_BEGIN_ALLOW_THREADS;
316
  self->ptr->get_nns_by_item(item, n, search_k, &result, include_distances ? &distances : NULL);
317
  Py_END_ALLOW_THREADS;
318

319
  return get_nns_to_python(result, distances, include_distances);
320
}
321

322

323
bool
324
convert_list_to_vector(PyObject* v, int f, vector<float>* w) {
325
  Py_ssize_t length = PyObject_Size(v);
326
  if (length == -1) {
327
    return false;
328
  }
329
  if (length != f) {
330
    PyErr_Format(PyExc_IndexError, "Vector has wrong length (expected %d, got %ld)", f, length);
331
    return false;
332
  }
333

334
  for (int z = 0; z < f; z++) {
335
    PyObject *key = PyInt_FromLong(z);
336
    if (key == NULL) {
337
      return false;
338
    }
339
    PyObject *pf = PyObject_GetItem(v, key);
340
    Py_DECREF(key);
341
    if (pf == NULL) {
342
      return false;
343
    }
344
    double value = PyFloat_AsDouble(pf);
345
    Py_DECREF(pf);
346
    if (value == -1.0 && PyErr_Occurred()) {
347
      return false;
348
    }
349
    (*w)[z] = value;
350
  }
351
  return true;
352
}
353

354
static PyObject* 
355
py_an_get_nns_by_vector(py_annoy *self, PyObject *args, PyObject *kwargs) {
356
  PyObject* v;
357
  int32_t n, search_k=-1, include_distances=0;
358
  if (!self->ptr) 
359
    return NULL;
360

361
  static char const * kwlist[] = {"vector", "n", "search_k", "include_distances", NULL};
362
  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Oi|ii", (char**)kwlist, &v, &n, &search_k, &include_distances))
363
    return NULL;
364

365
  vector<float> w(self->f);
366
  if (!convert_list_to_vector(v, self->f, &w)) {
367
    return NULL;
368
  }
369

370
  vector<int32_t> result;
371
  vector<float> distances;
372

373
  Py_BEGIN_ALLOW_THREADS;
374
  self->ptr->get_nns_by_vector(&w[0], n, search_k, &result, include_distances ? &distances : NULL);
375
  Py_END_ALLOW_THREADS;
376

377
  return get_nns_to_python(result, distances, include_distances);
378
}
379

380

381
static PyObject* 
382
py_an_get_item_vector(py_annoy *self, PyObject *args) {
383
  int32_t item;
384
  if (!self->ptr) 
385
    return NULL;
386
  if (!PyArg_ParseTuple(args, "i", &item))
387
    return NULL;
388

389
  if (!check_constraints(self, item, false)) {
390
    return NULL;
391
  }
392

393
  vector<float> v(self->f);
394
  self->ptr->get_item(item, &v[0]);
395
  PyObject* l = PyList_New(self->f);
396
  if (l == NULL) {
397
    return NULL;
398
  }
399
  for (int z = 0; z < self->f; z++) {
400
    PyObject* dist = PyFloat_FromDouble(v[z]);
401
    if (dist == NULL) {
402
      goto error;
403
    }
404
    PyList_SetItem(l, z, dist);
405
  }
406

407
  return l;
408

409
  error:
410
    Py_XDECREF(l);
411
    return NULL;
412
}
413

414

415
static PyObject* 
416
py_an_add_item(py_annoy *self, PyObject *args, PyObject* kwargs) {
417
  PyObject* v;
418
  int32_t item;
419
  if (!self->ptr) 
420
    return NULL;
421
  static char const * kwlist[] = {"i", "vector", NULL};
422
  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iO", (char**)kwlist, &item, &v))
423
    return NULL;
424

425
  if (!check_constraints(self, item, true)) {
426
    return NULL;
427
  }
428

429
  vector<float> w(self->f);
430
  if (!convert_list_to_vector(v, self->f, &w)) {
431
    return NULL;
432
  }
433
  char* error;
434
  if (!self->ptr->add_item(item, &w[0], &error)) {
435
    PyErr_SetString(PyExc_Exception, error);
436
    free(error);
437
    return NULL;
438
  }
439

440
  Py_RETURN_NONE;
441
}
442

443
static PyObject *
444
py_an_on_disk_build(py_annoy *self, PyObject *args, PyObject *kwargs) {
445
  char *filename, *error;
446
  if (!self->ptr)
447
    return NULL;
448
  static char const * kwlist[] = {"fn", NULL};
449
  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", (char**)kwlist, &filename))
450
    return NULL;
451

452
  if (!self->ptr->on_disk_build(filename, &error)) {
453
    PyErr_SetString(PyExc_IOError, error);
454
    free(error);
455
    return NULL;
456
  }
457
  Py_RETURN_TRUE;
458
}
459

460
static PyObject *
461
py_an_build(py_annoy *self, PyObject *args, PyObject *kwargs) {
462
  int q;
463
  int n_jobs = -1;
464
  if (!self->ptr) 
465
    return NULL;
466
  static char const * kwlist[] = {"n_trees", "n_jobs", NULL};
467
  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|i", (char**)kwlist, &q, &n_jobs))
468
    return NULL;
469

470
  bool res;
471
  char* error;
472
  Py_BEGIN_ALLOW_THREADS;
473
  res = self->ptr->build(q, n_jobs, &error);
474
  Py_END_ALLOW_THREADS;
475
  if (!res) {
476
    PyErr_SetString(PyExc_Exception, error);
477
    free(error);
478
    return NULL;
479
  }
480

481
  Py_RETURN_TRUE;
482
}
483

484

485
static PyObject *
486
py_an_unbuild(py_annoy *self) {
487
  if (!self->ptr) 
488
    return NULL;
489

490
  char* error;
491
  if (!self->ptr->unbuild(&error)) {
492
    PyErr_SetString(PyExc_Exception, error);
493
    free(error);
494
    return NULL;
495
  }
496

497
  Py_RETURN_TRUE;
498
}
499

500

501
static PyObject *
502
py_an_unload(py_annoy *self) {
503
  if (!self->ptr) 
504
    return NULL;
505

506
  self->ptr->unload();
507

508
  Py_RETURN_TRUE;
509
}
510

511

512
static PyObject *
513
py_an_get_distance(py_annoy *self, PyObject *args) {
514
  int32_t i, j;
515
  if (!self->ptr) 
516
    return NULL;
517
  if (!PyArg_ParseTuple(args, "ii", &i, &j))
518
    return NULL;
519

520
  if (!check_constraints(self, i, false) || !check_constraints(self, j, false)) {
521
    return NULL;
522
  }
523

524
  double d = self->ptr->get_distance(i,j);
525
  return PyFloat_FromDouble(d);
526
}
527

528

529
static PyObject *
530
py_an_get_n_items(py_annoy *self) {
531
  if (!self->ptr) 
532
    return NULL;
533

534
  int32_t n = self->ptr->get_n_items();
535
  return PyInt_FromLong(n);
536
}
537

538
static PyObject *
539
py_an_get_n_trees(py_annoy *self) {
540
  if (!self->ptr) 
541
    return NULL;
542

543
  int32_t n = self->ptr->get_n_trees();
544
  return PyInt_FromLong(n);
545
}
546

547
static PyObject *
548
py_an_verbose(py_annoy *self, PyObject *args) {
549
  int verbose;
550
  if (!self->ptr) 
551
    return NULL;
552
  if (!PyArg_ParseTuple(args, "i", &verbose))
553
    return NULL;
554

555
  self->ptr->verbose((bool)verbose);
556

557
  Py_RETURN_TRUE;
558
}
559

560

561
static PyObject *
562
py_an_set_seed(py_annoy *self, PyObject *args) {
563
  int q;
564
  if (!self->ptr)
565
    return NULL;
566
  if (!PyArg_ParseTuple(args, "i", &q))
567
    return NULL;
568

569
  self->ptr->set_seed(q);
570

571
  Py_RETURN_NONE;
572
}
573

574

575
static PyMethodDef AnnoyMethods[] = {
576
  {"load",	(PyCFunction)py_an_load, METH_VARARGS | METH_KEYWORDS, "Loads (mmaps) an index from disk."},
577
  {"save",	(PyCFunction)py_an_save, METH_VARARGS | METH_KEYWORDS, "Saves the index to disk."},
578
  {"get_nns_by_item",(PyCFunction)py_an_get_nns_by_item, METH_VARARGS | METH_KEYWORDS, "Returns the `n` closest items to item `i`.\n\n:param search_k: the query will inspect up to `search_k` nodes.\n`search_k` gives you a run-time tradeoff between better accuracy and speed.\n`search_k` defaults to `n_trees * n` if not provided.\n\n:param include_distances: If `True`, this function will return a\n2 element tuple of lists. The first list contains the `n` closest items.\nThe second list contains the corresponding distances."},
579
  {"get_nns_by_vector",(PyCFunction)py_an_get_nns_by_vector, METH_VARARGS | METH_KEYWORDS, "Returns the `n` closest items to vector `vector`.\n\n:param search_k: the query will inspect up to `search_k` nodes.\n`search_k` gives you a run-time tradeoff between better accuracy and speed.\n`search_k` defaults to `n_trees * n` if not provided.\n\n:param include_distances: If `True`, this function will return a\n2 element tuple of lists. The first list contains the `n` closest items.\nThe second list contains the corresponding distances."},
580
  {"get_item_vector",(PyCFunction)py_an_get_item_vector, METH_VARARGS, "Returns the vector for item `i` that was previously added."},
581
  {"add_item",(PyCFunction)py_an_add_item, METH_VARARGS | METH_KEYWORDS, "Adds item `i` (any nonnegative integer) with vector `v`.\n\nNote that it will allocate memory for `max(i)+1` items."},
582
  {"on_disk_build",(PyCFunction)py_an_on_disk_build, METH_VARARGS | METH_KEYWORDS, "Build will be performed with storage on disk instead of RAM."},
583
  {"build",(PyCFunction)py_an_build, METH_VARARGS | METH_KEYWORDS, "Builds a forest of `n_trees` trees.\n\nMore trees give higher precision when querying. After calling `build`,\nno more items can be added. `n_jobs` specifies the number of threads used to build the trees. `n_jobs=-1` uses all available CPU cores."},
584
  {"unbuild",(PyCFunction)py_an_unbuild, METH_NOARGS, "Unbuilds the tree in order to allows adding new items.\n\nbuild() has to be called again afterwards in order to\nrun queries."},
585
  {"unload",(PyCFunction)py_an_unload, METH_NOARGS, "Unloads an index from disk."},
586
  {"get_distance",(PyCFunction)py_an_get_distance, METH_VARARGS, "Returns the distance between items `i` and `j`."},
587
  {"get_n_items",(PyCFunction)py_an_get_n_items, METH_NOARGS, "Returns the number of items in the index."},
588
  {"get_n_trees",(PyCFunction)py_an_get_n_trees, METH_NOARGS, "Returns the number of trees in the index."},
589
  {"verbose",(PyCFunction)py_an_verbose, METH_VARARGS, ""},
590
  {"set_seed",(PyCFunction)py_an_set_seed, METH_VARARGS, "Sets the seed of Annoy's random number generator."},
591
  {NULL, NULL, 0, NULL}		 /* Sentinel */
592
};
593

594

595
static PyTypeObject PyAnnoyType = {
596
  PyVarObject_HEAD_INIT(NULL, 0)
597
  "annoy.Annoy",          /*tp_name*/
598
  sizeof(py_annoy),       /*tp_basicsize*/
599
  0,                      /*tp_itemsize*/
600
  (destructor)py_an_dealloc, /*tp_dealloc*/
601
  0,                      /*tp_print*/
602
  0,                      /*tp_getattr*/
603
  0,                      /*tp_setattr*/
604
  0,                      /*tp_compare*/
605
  0,                      /*tp_repr*/
606
  0,                      /*tp_as_number*/
607
  0,                      /*tp_as_sequence*/
608
  0,                      /*tp_as_mapping*/
609
  0,                      /*tp_hash */
610
  0,                      /*tp_call*/
611
  0,                      /*tp_str*/
612
  0,                      /*tp_getattro*/
613
  0,                      /*tp_setattro*/
614
  0,                      /*tp_as_buffer*/
615
  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
616
  ANNOY_DOC,              /* tp_doc */
617
  0,                      /* tp_traverse */
618
  0,                      /* tp_clear */
619
  0,                      /* tp_richcompare */
620
  0,                      /* tp_weaklistoffset */
621
  0,                      /* tp_iter */
622
  0,                      /* tp_iternext */
623
  AnnoyMethods,           /* tp_methods */
624
  py_annoy_members,       /* tp_members */
625
  0,                      /* tp_getset */
626
  0,                      /* tp_base */
627
  0,                      /* tp_dict */
628
  0,                      /* tp_descr_get */
629
  0,                      /* tp_descr_set */
630
  0,                      /* tp_dictoffset */
631
  (initproc)py_an_init,   /* tp_init */
632
  0,                      /* tp_alloc */
633
  py_an_new,              /* tp_new */
634
};
635

636
static PyMethodDef module_methods[] = {
637
  {NULL}	/* Sentinel */
638
};
639

640
#if PY_MAJOR_VERSION >= 3
641
  static struct PyModuleDef moduledef = {
642
    PyModuleDef_HEAD_INIT,
643
    "annoylib",          /* m_name */
644
    ANNOY_DOC,           /* m_doc */
645
    -1,                  /* m_size */
646
    module_methods,      /* m_methods */
647
    NULL,                /* m_reload */
648
    NULL,                /* m_traverse */
649
    NULL,                /* m_clear */
650
    NULL,                /* m_free */
651
  };
652
#endif
653

654
PyObject *create_module(void) {
655
  PyObject *m;
656

657
  if (PyType_Ready(&PyAnnoyType) < 0)
658
    return NULL;
659

660
#if PY_MAJOR_VERSION >= 3
661
  m = PyModule_Create(&moduledef);
662
#else
663
  m = Py_InitModule("annoylib", module_methods);
664
#endif
665

666
  if (m == NULL)
667
    return NULL;
668

669
  Py_INCREF(&PyAnnoyType);
670
  PyModule_AddObject(m, "Annoy", (PyObject *)&PyAnnoyType);
671
  return m;
672
}
673

674
#if PY_MAJOR_VERSION >= 3
675
  PyMODINIT_FUNC PyInit_annoylib(void) {
676
    return create_module();      // it should return moudule object in py3
677
  }
678
#else
679
  PyMODINIT_FUNC initannoylib(void) {
680
    create_module();
681
  }
682
#endif
683

684

685
// vim: tabstop=2 shiftwidth=2
686

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.