Я чувствую, что вы вносите различия, которых на самом деле не существует. На самом деле иерархии как таковой нет. В питоне все является объектом. Это не какое-то абстрактное понятие, но довольно фундаментально для того, как вы должны думать о конструкциях, которые вы создаете при использовании python. Объект - это просто куча других объектов. Существует небольшая тонкость в том, используете ли вы классы нового стиля или нет, но в отсутствие веской причины, в противном случае, просто используйте и примите классы нового стиля. Все ниже предполагает классы нового стиля.
Если объект является вызываемым , вы можете вызвать его, используя синтаксис вызова пары фигурных скобок с аргументами внутри них: my_callable(arg1, arg2)
. Чтобы быть вызываемым, объект должен реализовать метод __call__
(или же в его определении типа уровня C должно быть установлено правильное поле).
В python объект имеет type
, связанный с ним. Тип описывает, как был построен объект. Так, например, объект списка имеет тип list
, а объект функции имеет тип function
. Сами типы имеют тип type
. Вы можете найти тип с помощью встроенной функции type()
. Список всех встроенных типов можно найти в документации по питону . Типы фактически являются вызываемыми объектами и используются для создания экземпляров данного типа.
Правильно, теперь это установлено, природа данного объекта определяется его типом. Это описывает объекты, из которых оно состоит. Возвращаясь к вашим вопросам тогда:
Во-первых, группа объектов, составляющих некоторый объект, называется атрибутами этого объекта. Эти атрибуты могут быть любыми, но обычно они состоят из методов и некоторого способа хранения состояния (это могут быть такие типы, как int
или list
).
A функция является объектом типа function
. Важно то, что он имеет метод __call__
в качестве атрибута, который делает его вызываемым (метод __call__
также является объектом, который сам имеет метод __call__
. Он __call__
полностью вниз;)
A class в мире Python может рассматриваться как тип, но обычно используется для ссылки на типы, которые не являются встроенными. Эти объекты используются для создания других объектов. Вы можете определить свои собственные классы с помощью ключевого слова class, а для создания класса нового стиля вы должны наследовать от object
(или какого-либо другого класса нового стиля). Когда вы наследуете, вы создаете тип, который приобретает все характеристики родительского типа, а затем вы можете перезаписать биты, которые хотите (и вы можете перезаписать любые биты, которые вы хотите!). Когда вы создаете экземпляр класса (или, в более общем случае, тип), вызывая его, возвращается другой объект, созданный этим классом (способ создания возвращаемого объекта может быть изменен странным и безумным образом путем изменения объекта класса).
A метод - это особый тип функции, который вызывается с использованием нотации атрибута. То есть при его создании в метод добавляются 2 дополнительных атрибута (помните, что это объект!) С именами im_self
и im_func
. im_self
Я опишу в нескольких предложениях. im_func
- это функция, которая реализует метод. Когда метод вызывается, как, например, foo.my_method(10)
, это эквивалентно вызову foo.my_method.im_func(im_self, 10)
. Вот почему, когда вы определяете метод, вы определяете его с помощью дополнительного первого аргумента, который, по-видимому, вы не используете (как self
).
Когда вы пишете несколько методов при определении класса, они становятся несвязанными методами. Когда вы создаете экземпляр этого класса, эти методы становятся bound . Когда вы вызываете связанный метод, для вас добавляется аргумент im_self
как объект, в котором находится связанный метод. Вы все еще можете вызвать несвязанный метод класса, но вам необходимо явно добавить экземпляр класса в качестве первого аргумента:
class Foo(object):
def bar(self):
print self
print self.bar
print self.bar.im_self # prints the same as self
Мы можем показать, что происходит, когда мы вызываем различные проявления метода bar:
>>> a = Foo()
>>> a.bar()
<__main__.Foo object at 0x179b610>
<bound method Foo.bar of <__main__.Foo object at 0x179b610>>
<__main__.Foo object at 0x179b610>
>>> Foo.bar()
TypeError: unbound method bar() must be called with Foo instance as first argument (got nothing instead)
>>> Foo.bar(a)
<__main__.Foo object at 0x179b610>
<bound method Foo.bar of <__main__.Foo object at 0x179b610>>
<__main__.Foo object at 0x179b610>
Собрав все вышеперечисленное, мы можем определить класс следующим образом:
class MyFoo(object):
a = 10
def bar(self):
print self.a
Генерирует класс с 2 атрибутами: a
(который является целым числом значения 10) и bar
, который является несвязанным методом. Мы видим, что MyFoo.a
это просто 10.
Мы можем создавать дополнительные атрибуты во время выполнения, как внутри методов класса, так и снаружи. Учтите следующее:
class MyFoo(object):
a = 10
def __init__(self):
self.b = 20
def bar(self):
print self.a
print self.b
def eep(self):
print self.c
__init__
- это просто метод, который вызывается сразу после создания объекта из класса.
>>> foo = Foo()
>>> foo.bar()
10
20
>>> foo.eep()
AttributeError: 'MyFoo' object has no attribute 'c'
>>> foo.c = 30
>>> foo.eep()
30
В этом примере показано 2 способа добавления атрибута к экземпляру класса во время выполнения (то есть после того, как объект был создан из его класса).
Надеюсь, вы увидите, что TestCase и TestSuite - это просто классы, которые используются для создания тестовых объектов. В них нет ничего особенного, кроме того, что у них есть некоторые полезные функции для написания тестов. Вы можете создавать подклассы и перезаписывать их на свое усмотрение!
Что касается вашей конкретной точки, и методы, и функции могут возвращать все, что они захотят.
Ваше описание модуля, пакета и пакета выглядит довольно убедительно. Обратите внимание, что модули также являются объектами!