2015-06-20 1 views
4

Что было бы эквивалентом указателя элемента C++ в Python? В принципе, я хотел бы быть в состоянии воспроизвести подобное поведение в Python:Python эквивалент указателя элемента C++

// Pointer to a member of MyClass 
int (MyClass::*ptMember)(int) = &MyClass::member; 

// Call member on some instance, e.g. inside a function to 
// which the member pointer was passed 
instance.*ptMember(3) 

Развейте вопрос, что если член является свойством вместо метода? Можно ли сохранить/передать «указатель» на свойство без указания экземпляра?

Одним из способов, очевидно, является передача строки и использование eval. Но есть ли более чистый способ?

EDIT: В настоящее время существует несколько действительно хороших ответов, каждый из которых имеет что-то полезное в зависимости от контекста. Я закончил использовать то, что описано в моем ответе, но я думаю, что другие ответы будут очень полезны для тех, кто приходит сюда, на основе темы вопроса. Итак, я пока не принимаю ни одного.

+1

@ Dan, вы можете предложить решение, которое позволило бы мне сделать это, вставив код C++ в мое приложение python :) –

ответ

0

Способ, которым я подходил бы к этому в python, - использовать __getattribute__. Если у вас есть имя атрибута, который будет аналогом указателя-указателя C++, вы можете вызвать a.__getattribute__(x), чтобы получить атрибут, имя которого хранится в x. Это строки и dicts вместо смещений & указателей, но это python.

+0

Спасибо, это звучит как действительно хорошая альтернатива 'eval', если у вас есть string с именем метода. Тем не менее, из-за моего смещения на C++, я, вероятно, всегда найду не использую строки в таком контексте более симпатичные :) –

2

Я не был доволен строчным подходом и провел некоторые испытания. Это, кажется, работает очень хорошо и избегает прохождения строк вокруг:

import types 

# Our test class 
class Class: 

    def __init__(self, val): 
     self._val = val 

    def method(self): 
     return self._val 

    @property 
    def prop(self): 
     return self._val 

# Get the member pointer equivalents 
m = Class.method 
p = Class.prop 

# Create an instance 
c1 = Class(1) 

# Bind the method and property getter to the instance 
m1 = types.MethodType(m, c1) 
p1 = types.MethodType(p.fget, c1) 

# Use 
m1() # Returns 1 
p1() # Returns 1 

# Alternatively, the instance can be passed to the function as self 
m(c1) # Returns 1 
p.fget(c1) # Returns 1 
+1

Хотя это, вероятно, дает приближение указателей C++, это не кажется очень pythonic, и использовать такой язык, как this isn Это так. Итак, теперь, когда вы поняли, как это сделать, вы должны спросить себя ... Если вы это сделаете? ;) – Dan

+0

@Dan Итак, используя '__getattribute__' или' eval', по вашему мнению, более Pythonic? –

+0

И более Pythonic я не имею в виду, что «может быть сделано только в python», а скорее «лучший способ сделать это в python» :) –

1

Я не программист C++, так что, может быть, я пропускаю некоторые детали метода указателей здесь, но это звучит, как вы просто хотите ссылку функция, определенная внутри класса. (Это были типа instancemethod в Python 2, но просто введите function в Python 3.)

Синтаксис будет немного отличаться --- вместо того, чтобы называть его как метод с object.reference(args), вы будете называть его, как функция: reference(object, args). object будет аргументом для параметра self --- в значительной степени то, что сделал бы компилятор для вас.

Несмотря на более синтаксис C-like, я думаю, что он по-прежнему делает то, что вы хотели ... по крайней мере, когда применяется к вызываемому члену, как в вашем примере. Это не будет помочь с необязательным полем экземпляра: они не существуют до тех пор, пока не пройдет __init__.

Вот демонстрация:

#!/usr/bin/env python3 


import math 


class Vector(object): 

    def __init__(self, x, y): 
     self.x = x 
     self.y = y 
     return 

    def __str__(self): 
     return '(' + str(self.x) + ', ' + str(self.y) + ')' 

    def __repr__(self): 
     return self.__class__.__name__ + str(self) 

    def magnitude(self): 
     return math.sqrt(self.x ** 2 + self.y ** 2) 


def print_dict_getter_demo(): 
    print('Demo of member references on a Python dict:') 
    dict_getter = dict.get 
    d = {'a': 1, 'b': 2, 'c': 3, 'z': 26} 
    print('Dictionary d  : ' + str(d)) 
    print("d.get('a')  : " + str(d.get('a'))) 
    print("Ref to get 'a' : " + str(dict_getter(d, 'a'))) 
    print("Ref to get 'BOGUS': " + str(dict_getter(d, 'BOGUS'))) 
    print('Ref to get default: ' + str(dict_getter(d, 'BOGUS', 'not None'))) 
    return 


def print_vector_magnitude_demo(): 
    print('Demo of member references on a user-defined Vector:') 
    vector_magnitude = Vector.magnitude 
    v = Vector(3, 4) 
    print('Vector v  : ' + str(v)) 
    print('v.magnitude() : ' + str(v.magnitude())) 
    print('Ref to magnitude: ' + str(vector_magnitude(v))) 
    return 

def print_vector_sorting_demo(): 
    print('Demo of sorting Vectors using a member reference:') 
    vector_magnitude = Vector.magnitude 
    v0 = Vector(0, 0) 
    v1 = Vector(1, 1) 
    v5 = Vector(-3, -4) 
    v20 = Vector(-12, 16) 
    vector_list = [v20, v0, v5, v1] 
    print('Unsorted: ' + str(vector_list)) 
    sorted_vector_list = sorted(vector_list, key=vector_magnitude) 
    print('Sorted: ' + str(sorted_vector_list)) 
    return 


def main(): 
    print_dict_getter_demo() 
    print() 
    print_vector_magnitude_demo() 
    print() 
    print_vector_sorting_demo() 
    return 


if '__main__' == __name__: 
    main() 

При запуске с Python 3, это дает:

Demo of member references on a Python dict: 
Dictionary d  : {'a': 1, 'c': 3, 'b': 2, 'z': 26} 
d.get('a')  : 1 
Ref to get 'a' : 1 
Ref to get 'BOGUS': None 
Ref to get default: not None 

Demo of member references on a user-defined Vector: 
Vector v  : (3, 4) 
v.magnitude() : 5.0 
Ref to magnitude: 5.0 

Demo of sorting Vectors using a member reference: 
Unsorted: [Vector(-12, 16), Vector(0, 0), Vector(-3, -4), Vector(1, 1)] 
Sorted: [Vector(0, 0), Vector(1, 1), Vector(-3, -4), Vector(-12, 16)] 

Как вы можете видеть, это работает как и встроенных команд классов, определенных пользователем.

Edit:

Огромный демо выше было основано на предположении: что вы имели ссылку на класс, и что ваша цель состояла в том, чтобы «держаться» к одному из методов класса для использования о том, какие экземпляры этого класса появились позже.

Если у вас уже есть ссылка на экземпляр, это гораздо проще:

d = {'a': 1, 'b': 2, 'c': 3, 'z': 26} 
d_getter = d.get 
d_getter('z') # returns 26 

Это в основном то же самое, что и выше, только после преобразования из function в method была «заперта в «аргумент self, поэтому вам не нужно его подавать.

+0

Ваше предположение верно. Я хочу сохранить ссылку на метод, чтобы использовать его на каком-то другом экземпляре позже. Тем не менее, ваша демонстрация, вероятно, может быть упрощена :) Спасибо, хотя! –

5

Ближайшие подходит, вероятно, будет operator.attrgetter:

from operator import attrgetter 
foo_member = attrgetter('foo') 
bar_member = attrgetter('bar') 
baz_member = attrgetter('baz') 

class Example(object): 
    def __init__(self): 
     self.foo = 1 

    @property 
    def bar(self): 
     return 2 

    def baz(self): 
     return 3 

example_object = Example() 
print foo_member(example_object) # prints 1 
print bar_member(example_object) # prints 2 
print baz_member(example_object)() # prints 3 

attrgetter проходит точно такой же механизм нормальной пунктирная доступа проходит через, так что он работает для чего-либо вообще вы бы получить доступ с точкой. Поля, методы, члены модуля, динамически вычисленные атрибуты, что угодно. Неважно, какой тип объекта есть; например, attrgetter('count') может получить атрибут count списка, кортежа, строки или чего-либо еще с атрибутом count.


Для определенных типов атрибутов могут быть более конкретные вещи, похожие на указатели-указатели. Например, для методов экземпляра, вы можете получить несвязанный метод:

unbound_baz_method = Example.baz 
print unbound_baz_method(example_object) # prints 3 

Это либо специфическая функция, которая реализует метод, или очень тонкую оболочку вокруг функции, в зависимости от версии Python. Это тип-специфический; list.count не будет работать для кортежей, а tuple.count не будет работать для списков.

Для свойств, вы можете получить объект недвижимости-х fget, fset и fdel, которые являются функциями, которые реализуют получение, извлечение и удаление атрибута свойства управляют:

example_bar_member = Example.bar.fget 
print example_bar_member(example_object) # prints 2 

Мы не реализовали сеттер или дебетер для этого свойства, поэтому fset и fdel - None. Они также специфичны для конкретного типа; например, если example_bar_member правильно обработаны списки, example_bar_member([]) будет поднять AttributeError, а не возвращать 2, так как списки не имеют атрибута bar.

+0

Спасибо! Я не знал о существовании 'operator.attrgetter'. Это похоже на элегантную альтернативу тому, что у меня есть в моем ответе. Я по-прежнему предпочитаю просто ссылаться на несвязанный метод, хотя для свойств 'attrgetter' действительно выглядит как отличный способ, который не требует использования' .fget' и т. Д. –

6

Предполагая, класс Python:

class MyClass: 
    def __init__(self): 
     self.x = 42 

    def fn(self): 
     return self.x 

Эквивалент C++ указатель на memberfunction тогда это:

fn = MyClass.fn 

Вы можете назвать это, используя экземпляр, как и в C++:

o = MyClass() 
print(fn(o)) # prints 42 

Однако часто более интересной является тот факт, что вы также можете взять «адрес» связанного член функция, которая не работает в C++:

o = MyClass() 
bfn = o.fn 
print(bfn()) # prints 42, too 

Что касается последующих мер со свойствами, есть много ответов здесь уже, что решить эту проблему, если она все еще одна.

+0

Мое использование не позволяет мне иметь адрес связанного метода в первом но ваше предложение 'fn (o)' действительно выглядит как самый элегантный способ иметь указатель на элемент метода Python. Это очень похоже на подход 'MethodType', но более чистый, если впоследствии вам не нужно будет передавать связанный метод. –

+1

Я не могу представить, что вы там делаете; По моему опыту, мне всегда нужна связанная версия, поэтому мне пришлось использовать SigC++, Boost.Bind или более поздние версии C++ 11 и привязать функцию к объекту. Тем не менее, интересно, спасибо за отзывы! BTW, в Python функции и классы являются первоклассными объектами. Это означает, что вы можете присоединить функцию к классу и стать методом для всех экземпляров. Кроме того, вы можете взять метод из класса ('MyClass.fn' выше), и он станет простой функцией! Единственная разница между функцией и методом заключается в том, что первый параметр обычно называется 'self'! –