Подтвердить что ты не робот

Разница между методами и функциями

Я занимаюсь учебными курсами Code Academy на Python, и я немного запутался в определении метода и функции. Из учебника:

Вы уже знаете о некоторых встроенных функциях, которые мы использовали для (или создания) строк, таких как .upper(), .lower(), str() и len().

Исходя из С++, я бы подумал, что .upper() и .lower() будут называться методами и len() и str() функциями. В учебнике термины, по-видимому, используются взаимозаменяемо.

Разделяет ли Python методы и функции так, как это делает С++?

В отличие от Разница между методом и функцией, я спрашиваю о деталях Python. Термины "метод" и "функция", похоже, не всегда следуют определению, данному в принятом ответе связанного вопроса.

4b9b3361

Ответ 1

Функция является вызываемым объектом в Python, то есть может быть вызвана с помощью оператора вызова (хотя другие объекты могут эмулировать функцию путем реализации __call__). Например:

>>> def a(): pass
>>> a
<function a at 0x107063aa0>
>>> type(a)
<type 'function'>

Метод - это специальный класс функции, который может быть связан или несвязан.

>>> class A:
...   def a(self): pass
>>> A.a
<unbound method A.a>
>>> type(A.a)
<type 'instancemethod'>

>>> A().a
<bound method A.a of <__main__.A instance at 0x107070d88>>
>>> type(A().a)
<type 'instancemethod'>

Конечно, несвязанный метод не может быть вызван (по крайней мере, не напрямую, не передавая экземпляр в качестве аргумента):

>>> A.a()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method a() must be called with A instance as first argument (got nothing instead)

В Python в большинстве случаев вы не заметите разницу между связанным методом, функцией или вызываемым объектом (т.е. объектом, реализующим __call__), или конструктором класса. Все они выглядят одинаково, у них просто разные соглашения об именах. Под капотом объекты могут выглядеть значительно иначе.

Это означает, что связанный метод может использоваться как функция, это одна из многих мелких вещей, которая делает Python настолько мощным

>>> b = A().a
>>> b()

Это также означает, что, хотя существует фундаментальное различие между len(...) и str(...) (последний является конструктором типа), вы не заметите разницу, пока не будете копать немного глубже:

>>> len
<built-in function len>
>>> str
<type 'str'>

Ответ 2

Если вы все еще не понимаете, как работают методы, посмотрите на возможно, прояснить ситуацию. Когда атрибут экземпляра ссылается на то, что это не атрибут данных, его класс выполняется. Если имя обозначает действительный атрибут класса, являющийся объектом функции, объект метода создается путем упаковки (указатели на) объекта экземпляра и объект функции, только что найденный вместе в абстрактном объекте: это объект метода. Когда объект метода вызывается с список аргументов, новый список аргументов создается из экземпляра объект и список аргументов, а объект функции вызывается с этот новый список аргументов.

http://docs.python.org/2/tutorial/classes.html#method-objects

Внимательно прочитайте этот отрывок.

Это означает:

1/

Экземпляр действительно не удерживает объект как метод, который будет его атрибутом.
Фактически, в экземпляре __dict__ экземпляра нет атрибута "method" (__dict__ - пространство имен объекта)

2/

Тот факт, что экземпляр, кажется, имеет "метод" , когда вызывается атрибут "method", вызван процессом, а не наличием объекта метода внутри пространства имен экземпляра

3/

Кроме того, в пространстве имен класса не существует объекта метода.

Но есть ли разница с экземпляром, потому что должно быть что-то, что приводит к реальному объекту метода, когда такой вызов выполняется, не должен?

То, что называется атрибутом "метод" для класса, для удобства формулировки, на самом деле является функцией, являющимся атрибутом в пространстве имен класса.
Другими словами, пара (идентификатор функции, функции) является членом класса __dict__, и этот атрибут позволяет инспектору создавать объект метода при выполнении вызова метода.

4/

Опять же, тот факт, что класс, кажется, имеет "метод" , когда вызывается атрибут "метод" , обусловлен процессом, а не наличием объекта метода внутри пространства имен класса

EDIT Я не уверен в этом; см. в конце

5/

Объект метода (а не объект "метод" , я имею в виду, что реальный объект, являющийся действительно методом ", то, что описано в выдержке), создан в момент вызова, t существует раньше.
Это своего рода оболочка: она накладывает указатели на объект экземпляра и объект функции, на котором основан метод.

Итак, метод основан на функции. Эта функция для меня является истинным атрибутом класса, содержащего указанный "метод" , потому что эта функция действительно принадлежит пространству имен (__dict__) класса: эта функция описывается как <function ......>, когда __dict__ распечатаны.
Эта функция может быть достигнута из объекта метода с использованием псевдонима im_func или __func__ (см. Ниже код)

.

Я считаю, что эти понятия не очень широко известны и понятны. Но следующий код доказывает, что я сказал.

class A(object):
    def __init__(self,b=0):
        self.b = b
    print 'The __init__ object :\n',__init__

    def addu(self):
        self.b = self.b + 10
    print '\nThe addu object :\n',addu


print '\nThe A.__dict__  items :\n',
print '\n'.join('  {0:{align}11}  :  {1}'.format(*it,align='^')
                for it in A.__dict__.items())
a1 = A(101)
a2 = A(2002)

print '\nThe a1.__dict__  items:'
print '\n'.join('  {0:{align}11}  :  {1}'.format(*it,align='^')
                for it in a1.__dict__.items())

print '\nThe a2.__dict__  items:'
print '\n'.join('  {0:{align}11}  :  {1}'.format(*it,align='^')
                for it in a2.__dict__.items())

print '\nA.addu.__func__ :',A.addu.__func__
print id(A.addu.__func__),'==',hex(id(A.addu.__func__))
print

print 'A.addu :\n  ',
print A.addu,'\n  ',id(A.addu),'==',hex(id(A.addu))

print 'a1.addu :\n  ',
print a1.addu,'\n  ',id(a1.addu),'==',hex(id(a1.addu))
print 'a2.addu :\n  ',
print a2.addu,'\n  ',id(a2.addu),'==',hex(id(a2.addu))

a2.addu()
print '\na2.b ==',a2.b

print '\nThe A.__dict__  items :\n',
print '\n'.join('  {0:{align}11}  :  {1}'.format(*it,align='^')
                for it in A.__dict__.items())

результат

The __init__ object :
<function __init__ at 0x011E54B0>

The addu object :
<function addu at 0x011E54F0>

The A.__dict__  items :
  __module__   :  __main__
     addu      :  <function addu at 0x011E54F0>
   __dict__    :  <attribute '__dict__' of 'A' objects>
  __weakref__  :  <attribute '__weakref__' of 'A' objects>
    __doc__    :  None
   __init__    :  <function __init__ at 0x011E54B0>

The a1.__dict__  items:
       b       :  101

The a2.__dict__  items:
       b       :  2002

A.addu.__func__ : <function addu at 0x011E54F0>
18765040 == 0x11e54f0

A.addu :
   <unbound method A.addu> 
   18668040 == 0x11cda08
a1.addu :
   <bound method A.addu of <__main__.A object at 0x00CAA850>> 
   18668040 == 0x11cda08
a2.addu :
   <bound method A.addu of <__main__.A object at 0x011E2B90>> 
   18668040 == 0x11cda08

a2.b == 2012

The A.__dict__  items :
  __module__   :  __main__
     addu      :  <function addu at 0x011E54F0>
   __dict__    :  <attribute '__dict__' of 'A' objects>
  __weakref__  :  <attribute '__weakref__' of 'A' objects>
    __doc__    :  None
   __init__    :  <function __init__ at 0x011E54B0>

.

ИЗМЕНИТЬ

Что-то меня беспокоит, и я не знаю глубоких внутренностей предмета:

Вышеприведенный код показывает, что A.addu, a1.addu и a2.addu - все те же объекты метода, с уникальным идентификатором.
Однако A.addu называется несвязанным методом, поскольку он не имеет никакой информации о конкретном экземпляре,
и a1.addu и a2.addu являются связанными методами, поскольку каждый имеет информацию, обозначающую экземпляр, который должен быть связан с операциями метода.
Логически для меня это означало бы, что метод должен отличаться для каждого из этих трех случаев.

НО идентичность для всех трех одинакова, и, кроме того, это тождество отличается от тождества функции, на которой основан метод.
Это приводит к выводу, что метод действительно является объектом, живущим в памяти, и что он не изменяется от одного вызова от экземпляра к другому кал из другого экземпляра.

ОДНАКО, печатая пространство имен __dict__ класса, даже после создания экземпляров и вызова метода addu() это пространство имен не предоставляет новый объект, который может быть идентифицирован с объектом метода, отличным от функции addu.

Что это значит?
Создается впечатление, что как только объект метода создается, он не разрушается, он живет в памяти (ОЗУ).
Но он живет скрытым, и только процессы, которые формируют функтор-интерпретатор, знают, как и где его найти.
Этот скрытый объект, объект реального метода, должен иметь возможность изменять ссылку на экземпляр, к которому должна применяться эта функция, или ссылаться на None, если он вызывается как несвязанный метод. Это то, что мне кажется, но это только мозговая штурмовая гипотеза.

Кто-нибудь знает что-то на этом допросе?


Чтобы ответить на вопрос, можно считать корректным вызов функций .upper и .lower, поскольку в действительности они основаны на функциях как на каждом методе класса.

Однако следующий результат является особенным, вероятно, потому, что они встроены методы/функции, а не пользовательские методы/функции, как в моем коде.

x = 'hello'
print x.upper.__func__

результат

    print x.upper.__func__
AttributeError: 'builtin_function_or_method' object has no attribute '__func__'

Ответ 3

В следующем определении класса:

class MyClass:
    """A simple example class"""
    def f(self):
        return 'hello world'
  • Класс: MyClass
  • Функция: f()
  • Метод: Нет (на самом деле, не применимо)

Позволяет создать экземпляр вышеуказанного класса. Мы сделаем это, назначив class object, i.e. MyClass() var x

  x = MyClass()

Здесь

  • Функция: Нет
  • Метод: x.f()

И не забывайте, что function object MyClass.f использовался для определения (внутри) method object x.f, когда мы назначили x MyClass()

Ответ 4

В принципе, да, Python их отличает, но в Python обычно рассматривать методы как подмножество функций. Методы связаны с классом или экземпляром, а "автономные функции" - нет. Что-то, что является методом, также является функцией, но могут быть функции, которые не являются методами.

Как отметил Джон Клементс в своем комментарии, это различие не так сильно, как на С++. Автономные функции могут быть "преобразованы" в методы во время выполнения, а методы могут быть назначены переменным таким образом, чтобы они действовали не иначе, как автономные функции. Таким образом, граница между методами и функциями проницаема.