标签归档:oop

我应该使用类还是字典?

问题:我应该使用类还是字典?

我有一个只包含字段而没有方法的类,如下所示:

class Request(object):

    def __init__(self, environ):
        self.environ = environ
        self.request_method = environ.get('REQUEST_METHOD', None)
        self.url_scheme = environ.get('wsgi.url_scheme', None)
        self.request_uri = wsgiref.util.request_uri(environ)
        self.path = environ.get('PATH_INFO', None)
        # ...

这可以很容易地翻译成字典。该类对于将来的添加更加灵活,使用可以更快__slots__。那么使用dict会有好处吗?字典会比全班更快吗?并且比具有插槽的类快吗?

I have a class that contains only fields and no methods, like this:

class Request(object):

    def __init__(self, environ):
        self.environ = environ
        self.request_method = environ.get('REQUEST_METHOD', None)
        self.url_scheme = environ.get('wsgi.url_scheme', None)
        self.request_uri = wsgiref.util.request_uri(environ)
        self.path = environ.get('PATH_INFO', None)
        # ...

This could easily be translated to a dict. The class is more flexible for future additions and could be fast with __slots__. So would there be a benefit of using a dict instead? Would a dict be faster than a class? And faster than a class with slots?


回答 0

你为什么要把它当作字典?有什么好处?如果您以后想要添加一些代码,会发生什么?您的__init__代码会去哪儿?

类用于捆绑相关数据(通常是代码)。

字典用于存储键-值关系,其中通常键都是同一类型,并且所有值也都是一种类型。有时,当键/属性名称并非一开始就为人所知时,它们对于捆绑数据很有用,但这通常表明您的设计有问题。

保持这堂课。

Why would you make this a dictionary? What’s the advantage? What happens if you later want to add some code? Where would your __init__ code go?

Classes are for bundling related data (and usually code).

Dictionaries are for storing key-value relationships, where usually the keys are all of the same type, and all the values are also of one type. Occasionally they can be useful for bundling data when the key/attribute names are not all known up front, but often this a sign that something’s wrong with your design.

Keep this a class.


回答 1

使用字典,除非您需要类的额外机制。您还可以将a namedtuple用作混合方法:

>>> from collections import namedtuple
>>> request = namedtuple("Request", "environ request_method url_scheme")
>>> request
<class '__main__.Request'>
>>> request.environ = "foo"
>>> request.environ
'foo'

这里的性能差异将是最小的,尽管如果字典速度不快,我会感到惊讶。

Use a dictionary unless you need the extra mechanism of a class. You could also use a namedtuple for a hybrid approach:

>>> from collections import namedtuple
>>> request = namedtuple("Request", "environ request_method url_scheme")
>>> request
<class '__main__.Request'>
>>> request.environ = "foo"
>>> request.environ
'foo'

Performance differences here will be minimal, although I would be surprised if the dictionary wasn’t faster.


回答 2

python 的类下面的字典。类的行为确实会增加一些开销,但是如果没有事件探查器,您将无法注意到它。在这种情况下,我相信您会从课堂中受益,因为:

  • 您所有的逻辑都存在于一个功能中
  • 易于更新并保持封装
  • 如果以后更改任何内容,则可以轻松地使界面保持不变

A class in python is a dict underneath. You do get some overhead with the class behavior, but you won’t be able to notice it without a profiler. In this case, I believe you benefit from the class because:

  • All your logic lives in a single function
  • It is easy to update and stays encapsulated
  • If you change anything later, you can easily keep the interface the same

回答 3

我认为每个人的用法都太主观,我无法理解,所以我只会坚持数字。

我比较了在dict,new_style类和带槽的new_style类中创建和更改变量所需的时间。

这是我用来测试的代码(虽然有点杂乱,但确实可以完成工作。)

import timeit

class Foo(object):

    def __init__(self):

        self.foo1 = 'test'
        self.foo2 = 'test'
        self.foo3 = 'test'

def create_dict():

    foo_dict = {}
    foo_dict['foo1'] = 'test'
    foo_dict['foo2'] = 'test'
    foo_dict['foo3'] = 'test'

    return foo_dict

class Bar(object):
    __slots__ = ['foo1', 'foo2', 'foo3']

    def __init__(self):

        self.foo1 = 'test'
        self.foo2 = 'test'
        self.foo3 = 'test'

tmit = timeit.timeit

print 'Creating...\n'
print 'Dict: ' + str(tmit('create_dict()', 'from __main__ import create_dict'))
print 'Class: ' + str(tmit('Foo()', 'from __main__ import Foo'))
print 'Class with slots: ' + str(tmit('Bar()', 'from __main__ import Bar'))

print '\nChanging a variable...\n'

print 'Dict: ' + str((tmit('create_dict()[\'foo3\'] = "Changed"', 'from __main__ import create_dict') - tmit('create_dict()', 'from __main__ import create_dict')))
print 'Class: ' + str((tmit('Foo().foo3 = "Changed"', 'from __main__ import Foo') - tmit('Foo()', 'from __main__ import Foo')))
print 'Class with slots: ' + str((tmit('Bar().foo3 = "Changed"', 'from __main__ import Bar') - tmit('Bar()', 'from __main__ import Bar')))

这是输出…

正在建立…

Dict: 0.817466186345
Class: 1.60829183597
Class_with_slots: 1.28776730003

更改变量…

Dict: 0.0735140918748
Class: 0.111714198313
Class_with_slots: 0.10618612142

因此,如果您只是存储变量,则需要速度,并且不需要进行很多计算,因此我建议使用dict(您始终可以使函数看起来像方法)。但是,如果您确实需要类,请记住-始终使用__ slot __

注意:

我测试的“类”有两种 new_style和old_style类。事实证明,old_style类的创建速度更快,但修改速度却较慢(如果要在紧密的循环中创建许多类,则幅度不大,但意义重大(提示:您做错了))。

此外,由于我的计算机较旧且运行缓慢,因此在计算机上创建和更改变量的时间可能会有所不同。确保自己进行测试以查看“真实”结果。

编辑:

后来我测试了namedtuple:我无法修改它,但是创建10000个样本(或类似的东西)花了1.4秒,因此字典确实是最快的。

如果我更改dict函数以包括键和值,并在创建它时返回dict而不是包含dict的变量,则它会给我0.65而不是0.8秒。

class Foo(dict):
    pass

创建就像是一个带有插槽的类,并且更改变量最慢(0.17秒),因此不要使用这些类。求字典(速度)或对象派生的类(“语法糖果”)

I think that the usage of each one is way too subjective for me to get in on that, so i’ll just stick to numbers.

I compared the time it takes to create and to change a variable in a dict, a new_style class and a new_style class with slots.

Here’s the code i used to test it(it’s a bit messy but it does the job.)

import timeit

class Foo(object):

    def __init__(self):

        self.foo1 = 'test'
        self.foo2 = 'test'
        self.foo3 = 'test'

def create_dict():

    foo_dict = {}
    foo_dict['foo1'] = 'test'
    foo_dict['foo2'] = 'test'
    foo_dict['foo3'] = 'test'

    return foo_dict

class Bar(object):
    __slots__ = ['foo1', 'foo2', 'foo3']

    def __init__(self):

        self.foo1 = 'test'
        self.foo2 = 'test'
        self.foo3 = 'test'

tmit = timeit.timeit

print 'Creating...\n'
print 'Dict: ' + str(tmit('create_dict()', 'from __main__ import create_dict'))
print 'Class: ' + str(tmit('Foo()', 'from __main__ import Foo'))
print 'Class with slots: ' + str(tmit('Bar()', 'from __main__ import Bar'))

print '\nChanging a variable...\n'

print 'Dict: ' + str((tmit('create_dict()[\'foo3\'] = "Changed"', 'from __main__ import create_dict') - tmit('create_dict()', 'from __main__ import create_dict')))
print 'Class: ' + str((tmit('Foo().foo3 = "Changed"', 'from __main__ import Foo') - tmit('Foo()', 'from __main__ import Foo')))
print 'Class with slots: ' + str((tmit('Bar().foo3 = "Changed"', 'from __main__ import Bar') - tmit('Bar()', 'from __main__ import Bar')))

And here is the output…

Creating…

Dict: 0.817466186345
Class: 1.60829183597
Class_with_slots: 1.28776730003

Changing a variable…

Dict: 0.0735140918748
Class: 0.111714198313
Class_with_slots: 0.10618612142

So, if you’re just storing variables, you need speed, and it won’t require you to do many calculations, i recommend using a dict(you could always just make a function that looks like a method). But, if you really need classes, remember – always use __slots__.

Note:

I tested the ‘Class’ with both new_style and old_style classes. It turns out that old_style classes are faster to create but slower to modify(not by much but significant if you’re creating lots of classes in a tight loop (tip: you’re doing it wrong)).

Also the times for creating and changing variables may differ on your computer since mine is old and slow. Make sure you test it yourself to see the ‘real’ results.

Edit:

I later tested the namedtuple: i can’t modify it but to create the 10000 samples (or something like that) it took 1.4 seconds so the dictionary is indeed the fastest.

If i change the dict function to include the keys and values and to return the dict instead of the variable containing the dict when i create it it gives me 0.65 instead of 0.8 seconds.

class Foo(dict):
    pass

Creating is like a class with slots and changing the variable is the slowest (0.17 seconds) so do not use these classes. go for a dict (speed) or for the class derived from object (‘syntax candy’)


回答 4

我同意@adw。我永远不会用字典来代表“对象”(从OO意义上来说)。词典汇总名称/值对。类代表对象。我已经看到了用字典表示对象的代码,目前尚不清楚事物的实际形状是什么。当某些名称/值不存在时会发生什么?是什么限制了客户什么也没花。或者试图把所有东西都花掉。事物的形状应始终明确定义。

使用Python时,重要的是要有纪律性进行构建,因为该语言为作者提供了多种射击方式。

I agree with @adw. I would never represent an “object” (in an OO sense) with a dictionary. Dictionaries aggregate name/value pairs. Classes represent objects. I’ve seen code where the objects are represented with dictionaries and it’s unclear what the actual shape of the thing is. What happens when certain name/values aren’t there? What restricts the client from putting anything at all in. Or trying to get anything at all out. The shape of the thing should always be clearly defined.

When using Python it is important to build with discipline as the language allows many ways for the author to shoot him/herself in the foot.


回答 5

我会推荐一个类,因为它是与请求有关的各种信息。曾经是使用字典的人,我希望存储的数据本质上会更加相似。我倾向于遵循的一个指导原则是,如果我想遍历整个键-值对集合并执行某些操作,则可以使用字典。否则,数据显然比基本的键->值映射具有更多的结构,这意味着类可能是更好的选择。

因此,坚持上课。

I would recommend a class, as it is all sorts of information involved with a request. Were one to use a dictionary, I’d expect the data stored to be far more similar in nature. A guideline I tend to follow myself is that if I may want to loop over the entire set of key->value pairs and do something, I use a dictionary. Otherwise, the data apparently has far more structure than a basic key->value mapping, meaning a class would likely be a better alternative.

Hence, stick with the class.


回答 6

如果您要实现的只是语法糖果obj.bla = 5而不是obj['bla'] = 5,特别是如果您必须重复很多,那么您可能想要使用一些简单的容器类,如martineaus建议中那样。但是,那里的代码非常肿,并且速度很慢。您可以像这样简单:

class AttrDict(dict):
    """ Syntax candy """
    __getattr__ = dict.__getitem__
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

切换到namedtuples或class的另一个原因__slots__可能是内存使用率。字典比列表类型需要更多的内存,因此可能需要考虑一下。

无论如何,在您的特定情况下,似乎没有任何动力要退出当前的实现。您似乎没有维护数百万个此类对象,因此不需要列表派生类型。而且它实际上包含内的一些功能逻辑__init__,因此您也不应该使用AttrDict

If all that you want to achive is syntax candy like obj.bla = 5 instead of obj['bla'] = 5, especially if you have to repeat that a lot, you maybe want to use some plain container class as in martineaus suggestion. Nevertheless, the code there is quite bloated and unnecessarily slow. You can keep it simple like that:

class AttrDict(dict):
    """ Syntax candy """
    __getattr__ = dict.__getitem__
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

Another reason to switch to namedtuples or a class with __slots__ could be memory usage. Dicts require significantly more memory than list types, so this could be a point to think about.

Anyways, in your specific case, there doesn’t seem to be any motivation to switch away from your current implementation. You don’t seem to maintain millions of these objects, so no list-derived-types required. And it’s actually containing some functional logic within the __init__, so you also shouldn’t got with AttrDict.


回答 7

也可能有蛋糕也可以吃。换句话说,您可以创建提供类和字典实例功能的东西。请参阅ActiveState的Dɪᴄᴛɪᴏɴᴀʀʏᴡɪᴛʜᴀᴛᴛʀɪʙᴜᴛᴇ-sᴛʏʟᴇss食谱和有关此方法的注释。

如果您决定使用常规类而不是子类,那么我发现T recipesɪᴍᴘʟᴇᴄᴏʟʟᴇᴄᴛᴏʀᴄᴏʟʟᴇᴄᴛᴏʀᴄᴏʟʟᴇᴄᴛᴏʀrecipe recipe ss的食谱(由Alex Martelli 撰写非常灵活,对此类事情很有用看起来就像您在做的(即创建一个相对简单的信息聚合器)。由于它是一个类,因此您可以通过添加方法轻松地进一步扩展其功能。

最后,应该指出,类成员的名称必须是合法的Python标识符,但字典键则不能—因此字典在这方面将提供更大的自由度,因为键可以是任何可散列的东西(甚至可以不是字符串)。

更新资料

一类object(其不具有__dict__)指定的子类SimpleNamespace(它有一个)加入到该types模块的Python 3.3,并且是又一替代。

It may be possible to have your cake and eat it, too. In other words you can create something that provides the functionality of both a class and dictionary instance. See the ActiveState’s Dɪᴄᴛɪᴏɴᴀʀʏ ᴡɪᴛʜ ᴀᴛᴛʀɪʙᴜᴛᴇ-sᴛʏʟᴇ ᴀᴄᴄᴇss recipe and comments on ways of doing that.

If you decide to use a regular class rather than a subclass, I’ve found the Tʜᴇ sɪᴍᴘʟᴇ ʙᴜᴛ ʜᴀɴᴅʏ “ᴄᴏʟʟᴇᴄᴛᴏʀ ᴏғ ᴀ ʙᴜɴᴄʜ ᴏғ ɴᴀᴍᴇᴅ sᴛᴜғғ” ᴄʟᴀss recipe (by Alex Martelli) to be very flexible and useful for the sort of thing it looks like you’re doing (i.e. create a relative simple aggregator of information). Since it’s a class you can easily extend its functionality further by adding methods.

Lastly it should be noted that the names of class members must be legal Python identifiers, but dictionary keys do not—so a dictionary would provide greater freedom in that regard because keys can be anything hashable (even something that’s not a string).

Update

A class object (which doesn’t have a __dict__) subclass named SimpleNamespace (which does have one) was added to the types module Python 3.3, and is yet another alternative.


回答 8

class ClassWithSlotBase:
    __slots__ = ('a', 'b',)

def __init__(self):
    self.a: str = "test"
    self.b: float = 0.0


def test_type_hint(_b: float) -> None:
    print(_b)


class_tmp = ClassWithSlotBase()

test_type_hint(class_tmp.a)

我推荐一堂课。如果使用类,则可以得到如下所示的类型提示。当class是函数的参数时,Class支持自动完成。

class ClassWithSlotBase:
    __slots__ = ('a', 'b',)

def __init__(self):
    self.a: str = "test"
    self.b: float = 0.0


def test_type_hint(_b: float) -> None:
    print(_b)


class_tmp = ClassWithSlotBase()

test_type_hint(class_tmp.a)

I recommend a class. If you use a class, you can get type hint as shown. And Class support auto complete when class is argument of function.


python内部类的目的是什么?

问题:python内部类的目的是什么?

Python的内部/嵌套类使我感到困惑。没有他们,有什么事情是无法完成的吗?如果是这样,那是什么东西?

Python’s inner/nested classes confuse me. Is there something that can’t be accomplished without them? If so, what is that thing?


回答 0

引用自http://www.geekinterview.com/question_details/64739

内部阶层的优势:

  • 类的逻辑分组:如果一个类仅对另一个类有用,那么将其嵌入该类并将二者保持在一起是合乎逻辑的。嵌套此类“帮助程序类”可使它们的程序包更加简化。
  • 增加封装:考虑两个顶级类A和B,其中B需要访问A的成员,否则将其声明为私有。通过将B类隐藏在AA类中,可以将其成员声明为私有,B可以访问它们。另外,B本身可以对外界隐藏。
  • 更具可读性和可维护性的代码:在顶级类中嵌套小类会使代码更靠近使用位置。

主要优势是组织。可以用内部类实现什么没有他们来完成。

Quoted from http://www.geekinterview.com/question_details/64739:

Advantages of inner class:

  • Logical grouping of classes: If a class is useful to only one other class then it is logical to embed it in that class and keep the two together. Nesting such “helper classes” makes their package more streamlined.
  • Increased encapsulation: Consider two top-level classes A and B where B needs access to members of A that would otherwise be declared private. By hiding class B within class A A’s members can be declared private and B can access them. In addition B itself can be hidden from the outside world.
  • More readable, maintainable code: Nesting small classes within top-level classes places the code closer to where it is used.

The main advantage is organization. Anything that can be accomplished with inner classes can be accomplished without them.


回答 1

没有他们,有什么事情是无法完成的吗?

不。它们绝对等同于通常在顶层定义类,然后将对它的引用复制到外部类中。

我认为“允许”嵌套类没有任何特殊原因,除了明确禁止“禁止”嵌套类没有特殊意义。

如果您正在寻找一个在外部/所有者对象的生命周期内存在的类,并且始终引用外部类的实例(内部类就像Java一样),那么Python的嵌套类就不是问题。但是您可以破解类似的东西:

import weakref, new

class innerclass(object):
    """Descriptor for making inner classes.

    Adds a property 'owner' to the inner class, pointing to the outer
    owner instance.
    """

    # Use a weakref dict to memoise previous results so that
    # instance.Inner() always returns the same inner classobj.
    #
    def __init__(self, inner):
        self.inner= inner
        self.instances= weakref.WeakKeyDictionary()

    # Not thread-safe - consider adding a lock.
    #
    def __get__(self, instance, _):
        if instance is None:
            return self.inner
        if instance not in self.instances:
            self.instances[instance]= new.classobj(
                self.inner.__name__, (self.inner,), {'owner': instance}
            )
        return self.instances[instance]


# Using an inner class
#
class Outer(object):
    @innerclass
    class Inner(object):
        def __repr__(self):
            return '<%s.%s inner object of %r>' % (
                self.owner.__class__.__name__,
                self.__class__.__name__,
                self.owner
            )

>>> o1= Outer()
>>> o2= Outer()
>>> i1= o1.Inner()
>>> i1
<Outer.Inner inner object of <__main__.Outer object at 0x7fb2cd62de90>>
>>> isinstance(i1, Outer.Inner)
True
>>> isinstance(i1, o1.Inner)
True
>>> isinstance(i1, o2.Inner)
False

(这使用了类装饰器,这是Python 2.6和3.0中的新功能。否则,您必须在类定义之后说“ Inner = innerclass(Inner)”。)

Is there something that can’t be accomplished without them?

No. They are absolutely equivalent to defining the class normally at top level, and then copying a reference to it into the outer class.

I don’t think there’s any special reason nested classes are ‘allowed’, other than it makes no particular sense to explicitly ‘disallow’ them either.

If you’re looking for a class that exists within the lifecycle of the outer/owner object, and always has a reference to an instance of the outer class — inner classes as Java does it – then Python’s nested classes are not that thing. But you can hack up something like that thing:

import weakref, new

class innerclass(object):
    """Descriptor for making inner classes.

    Adds a property 'owner' to the inner class, pointing to the outer
    owner instance.
    """

    # Use a weakref dict to memoise previous results so that
    # instance.Inner() always returns the same inner classobj.
    #
    def __init__(self, inner):
        self.inner= inner
        self.instances= weakref.WeakKeyDictionary()

    # Not thread-safe - consider adding a lock.
    #
    def __get__(self, instance, _):
        if instance is None:
            return self.inner
        if instance not in self.instances:
            self.instances[instance]= new.classobj(
                self.inner.__name__, (self.inner,), {'owner': instance}
            )
        return self.instances[instance]


# Using an inner class
#
class Outer(object):
    @innerclass
    class Inner(object):
        def __repr__(self):
            return '<%s.%s inner object of %r>' % (
                self.owner.__class__.__name__,
                self.__class__.__name__,
                self.owner
            )

>>> o1= Outer()
>>> o2= Outer()
>>> i1= o1.Inner()
>>> i1
<Outer.Inner inner object of <__main__.Outer object at 0x7fb2cd62de90>>
>>> isinstance(i1, Outer.Inner)
True
>>> isinstance(i1, o1.Inner)
True
>>> isinstance(i1, o2.Inner)
False

(This uses class decorators, which are new in Python 2.6 and 3.0. Otherwise you’d have to say “Inner= innerclass(Inner)” after the class definition.)


回答 2

您需要包裹一些东西才能理解这一点。在大多数语言中,类定义是对编译器的指令。也就是说,该类是在程序运行之前创建的。在python中,所有语句都是可执行的。这意味着该语句:

class foo(object):
    pass

是一条在运行时执行的语句,如下所示:

x = y + z

这意味着您不仅可以在其他类中创建类,还可以在任意位置创建类。考虑以下代码:

def foo():
    class bar(object):
        ...
    z = bar()

因此,“内部类”的想法实际上不是一种语言构造;这是一个程序员构造。圭多拥有这是怎么围绕很好的总结在这里。但本质上,基本思想是简化了语言的语法。

There’s something you need to wrap your head around to be able to understand this. In most languages, class definitions are directives to the compiler. That is, the class is created before the program is ever run. In python, all statements are executable. That means that this statement:

class foo(object):
    pass

is a statement that is executed at runtime just like this one:

x = y + z

This means that not only can you create classes within other classes, you can create classes anywhere you want to. Consider this code:

def foo():
    class bar(object):
        ...
    z = bar()

Thus, the idea of an “inner class” isn’t really a language construct; it’s a programmer construct. Guido has a very good summary of how this came about here. But essentially, the basic idea is this simplifies the language’s grammar.


回答 3

在类中嵌套类:

  • 嵌套类使类定义变得肿,这使得很难看到发生了什么。

  • 嵌套类会创建耦合,从而使测试更加困难。

  • 在Python中,您可以在文件/模块中放置一个以上的类,这与Java不同,因此该类仍保持与顶级类的距离,甚至可以在类名前加上“ _”,以帮助表示不应将其他类使用它。

嵌套类可以证明有用的地方是函数内

def some_func(a, b, c):
   class SomeClass(a):
      def some_method(self):
         return b
   SomeClass.__doc__ = c
   return SomeClass

该类从函数中捕获值,使您可以动态创建一个类,例如C ++中的模板元编程

Nesting classes within classes:

  • Nested classes bloat the class definition making it harder to see whats going on.

  • Nested classes can create coupling that would make testing more difficult.

  • In Python you can put more than one class in a file/module, unlike Java, so the class still remains close to top level class and could even have the class name prefixed with an “_” to help signify that others shouldn’t be using it.

The place where nested classes can prove useful is within functions

def some_func(a, b, c):
   class SomeClass(a):
      def some_method(self):
         return b
   SomeClass.__doc__ = c
   return SomeClass

The class captures the values from the function allowing you to dynamically create a class like template metaprogramming in C++


回答 4

我了解反对嵌套类的参数,但是在某些情况下有使用它们的情况。想象一下,我正在创建一个双向链接列表类,并且需要创建一个节点类来维护节点。我有两个选择,在DoublyLinkedList类内部创建Node类,或在DoublyLinkedList类外部创建Node类。在这种情况下,我首选第一种选择,因为Node类仅在DoublyLinkedList类内部有意义。虽然没有隐藏/封装的好处,但是可以说Node类是DoublyLinkedList类的一部分,这有一个分组的好处。

I understand the arguments against nested classes, but there is a case for using them in some occasions. Imagine I’m creating a doubly-linked list class, and I need to create a node class for maintaing the nodes. I have two choices, create Node class inside the DoublyLinkedList class, or create the Node class outside the DoublyLinkedList class. I prefer the first choice in this case, because the Node class is only meaningful inside the DoublyLinkedList class. While there’s no hiding/encapsulation benefit, there is a grouping benefit of being able to say the Node class is part of the DoublyLinkedList class.


回答 5

没有他们,有什么事情是无法完成的吗?如果是这样,那是什么东西?

有以下一些事情是不容易完成的相关类的继承

这是相关类A和的极简示例B

class A(object):
    class B(object):
        def __init__(self, parent):
            self.parent = parent

    def make_B(self):
        return self.B(self)


class AA(A):  # Inheritance
    class B(A.B):  # Inheritance, same class name
        pass

这段代码导致了一个相当合理和可预测的行为:

>>> type(A().make_B())
<class '__main__.A.B'>
>>> type(A().make_B().parent)
<class '__main__.A'>
>>> type(AA().make_B())
<class '__main__.AA.B'>
>>> type(AA().make_B().parent)
<class '__main__.AA'>

如果B是顶级类,则不能self.B()在方法中make_B编写B(),而只会写,从而失去与适当类的动态绑定

请注意,在此构造中,您永远不要在class A主体中引用class B。这是parent在课堂上介绍该属性的动机B

当然,可以在没有内部类的情况下重新创建此动态绑定,而这会浪费乏味且易于出错的类。

Is there something that can’t be accomplished without them? If so, what is that thing?

There is something that cannot be easily done without: inheritance of related classes.

Here is a minimalist example with the related classes A and B:

class A(object):
    class B(object):
        def __init__(self, parent):
            self.parent = parent

    def make_B(self):
        return self.B(self)


class AA(A):  # Inheritance
    class B(A.B):  # Inheritance, same class name
        pass

This code leads to a quite reasonable and predictable behaviour:

>>> type(A().make_B())
<class '__main__.A.B'>
>>> type(A().make_B().parent)
<class '__main__.A'>
>>> type(AA().make_B())
<class '__main__.AA.B'>
>>> type(AA().make_B().parent)
<class '__main__.AA'>

If B were a top-level class, you could not write self.B() in the method make_B but would simply write B(), and thus lose the dynamic binding to the adequate classes.

Note that in this construction, you should never refer to class A in the body of class B. This is the motivation for introducing the parent attribute in class B.

Of course, this dynamic binding can be recreated without inner class at the cost of a tedious and error-prone instrumentation of the classes.


回答 6

我使用它的主要用例是防止小模块的扩散,在不需要单独的模块时防止命名空间污染。如果要扩展现有的类,但是该现有的类必须引用另一个应该始终与其耦合的子类。例如,我可能有一个utils.py其中包含许多帮助程序类的模块,这些模块不一定耦合在一起,但我想加强其中一些帮助程序类的耦合。例如,当我实现https://stackoverflow.com/a/8274307/2718295时

utils.py

import json, decimal

class Helper1(object):
    pass

class Helper2(object):
    pass

# Here is the notorious JSONEncoder extension to serialize Decimals to JSON floats
class DecimalJSONEncoder(json.JSONEncoder):

    class _repr_decimal(float): # Because float.__repr__ cannot be monkey patched
        def __init__(self, obj):
            self._obj = obj
        def __repr__(self):
            return '{:f}'.format(self._obj)

    def default(self, obj): # override JSONEncoder.default
        if isinstance(obj, decimal.Decimal):
            return self._repr_decimal(obj)
        # else
        super(self.__class__, self).default(obj)
        # could also have inherited from object and used return json.JSONEncoder.default(self, obj) 

然后,我们可以:

>>> from utils import DecimalJSONEncoder
>>> import json, decimal
>>> json.dumps({'key1': decimal.Decimal('1.12345678901234'), 
... 'key2':'strKey2Value'}, cls=DecimalJSONEncoder)
{"key2": "key2_value", "key_1": 1.12345678901234}

当然,我们可以完全避开继承,json.JSONEnocder而只需覆盖default()即可:

import decimal, json

class Helper1(object):
    pass

def json_encoder_decimal(obj):
    class _repr_decimal(float):
        ...

    if isinstance(obj, decimal.Decimal):
        return _repr_decimal(obj)

    return json.JSONEncoder(obj)


>>> json.dumps({'key1': decimal.Decimal('1.12345678901234')}, default=json_decimal_encoder)
'{"key1": 1.12345678901234}'

但有时只是出于约定,您希望utils由可扩展性的类组成。

这是另一个用例:我希望在OuterClass中创建一个用于可变项的工厂,而不必调用copy

class OuterClass(object):

    class DTemplate(dict):
        def __init__(self):
            self.update({'key1': [1,2,3],
                'key2': {'subkey': [4,5,6]})


    def __init__(self):
        self.outerclass_dict = {
            'outerkey1': self.DTemplate(),
            'outerkey2': self.DTemplate()}



obj = OuterClass()
obj.outerclass_dict['outerkey1']['key2']['subkey'].append(4)
assert obj.outerclass_dict['outerkey2']['key2']['subkey'] == [4,5,6]

我更喜欢这种模式,而@staticmethod不是原本用于工厂功能的装饰器。

The main use case I use this for is the prevent proliferation of small modules and to prevent namespace pollution when separate modules are not needed. If I am extending an existing class, but that existing class must reference another subclass that should always be coupled to it. For example, I may have a utils.py module that has many helper classes in it, that aren’t necessarily coupled together, but I want to reinforce coupling for some of those helper classes. For example, when I implement https://stackoverflow.com/a/8274307/2718295

:utils.py:

import json, decimal

class Helper1(object):
    pass

class Helper2(object):
    pass

# Here is the notorious JSONEncoder extension to serialize Decimals to JSON floats
class DecimalJSONEncoder(json.JSONEncoder):

    class _repr_decimal(float): # Because float.__repr__ cannot be monkey patched
        def __init__(self, obj):
            self._obj = obj
        def __repr__(self):
            return '{:f}'.format(self._obj)

    def default(self, obj): # override JSONEncoder.default
        if isinstance(obj, decimal.Decimal):
            return self._repr_decimal(obj)
        # else
        super(self.__class__, self).default(obj)
        # could also have inherited from object and used return json.JSONEncoder.default(self, obj) 

Then we can:

>>> from utils import DecimalJSONEncoder
>>> import json, decimal
>>> json.dumps({'key1': decimal.Decimal('1.12345678901234'), 
... 'key2':'strKey2Value'}, cls=DecimalJSONEncoder)
{"key2": "key2_value", "key_1": 1.12345678901234}

Of course, we could have eschewed inheriting json.JSONEnocder altogether and just override default():

:

import decimal, json

class Helper1(object):
    pass

def json_encoder_decimal(obj):
    class _repr_decimal(float):
        ...

    if isinstance(obj, decimal.Decimal):
        return _repr_decimal(obj)

    return json.JSONEncoder(obj)


>>> json.dumps({'key1': decimal.Decimal('1.12345678901234')}, default=json_decimal_encoder)
'{"key1": 1.12345678901234}'

But sometimes just for convention, you want utils to be composed of classes for extensibility.

Here’s another use-case: I want a factory for mutables in my OuterClass without having to invoke copy:

class OuterClass(object):

    class DTemplate(dict):
        def __init__(self):
            self.update({'key1': [1,2,3],
                'key2': {'subkey': [4,5,6]})


    def __init__(self):
        self.outerclass_dict = {
            'outerkey1': self.DTemplate(),
            'outerkey2': self.DTemplate()}



obj = OuterClass()
obj.outerclass_dict['outerkey1']['key2']['subkey'].append(4)
assert obj.outerclass_dict['outerkey2']['key2']['subkey'] == [4,5,6]

I prefer this pattern over the @staticmethod decorator you would otherwise use for a factory function.


回答 7

1.两种功能等效的方式

前面显示的两种方法在功能上是相同的。但是,有一些细微的差异,并且在某些情况下您想选择一个而不是另一个。

方式1:嵌套类定义
(=“ Nested class”)

class MyOuter1:
    class Inner:
        def show(self, msg):
            print(msg)

方式2:将模块级别的内部类附加到外部类
(=“ Referenced内部类”)

class _InnerClass:
    def show(self, msg):
        print(msg)

class MyOuter2:
    Inner = _InnerClass

下划线用于遵循PEP8: “内部接口(包,模块,类,函数,属性或其他名称)应-带有一个前导下划线作为前缀”。

2.相似之处

下面的代码片段演示了“嵌套类”与“引用内部类”的功能相似性;它们在检查内部类实例类型的代码中的行为方式相同。不用说,m.inner.anymethod()它们与m1和的行为类似m2

m1 = MyOuter1()
m2 = MyOuter2()

innercls1 = getattr(m1, 'Inner', None)
innercls2 = getattr(m2, 'Inner', None)

isinstance(innercls1(), MyOuter1.Inner)
# True

isinstance(innercls2(), MyOuter2.Inner)
# True

type(innercls1()) == mypackage.outer1.MyOuter1.Inner
# True (when part of mypackage)

type(innercls2()) == mypackage.outer2.MyOuter2.Inner
# True (when part of mypackage)

3.差异

下面列出了“嵌套类”和“引用内部类”的区别。它们并不大,但是有时您希望基于这些选择一个或另一个。

3.1代码封装

使用“嵌套类”可以比使用“引用内部类”更好地封装代码。模块命名空间中的类是全局变量。嵌套类的目的是减少模块中的混乱情况,并将内部类放入外部类中。

当没有人使用*时from packagename import *,少量模块级别的变量可能很好,例如,当使用具有代码完成/智能感知的IDE时。

* 对吗?

3.2代码的可读性

Django文档指示将内部类Meta用于模型元数据。指示框架用户class Foo(models.Model)使用inner 编写一个更清晰的* class Meta

class Ox(models.Model):
    horn_length = models.IntegerField()

    class Meta:
        ordering = ["horn_length"]
        verbose_name_plural = "oxen"

而不是“写class _Meta,然后写一个class Foo(models.Model)Meta = _Meta”;

class _Meta:
    ordering = ["horn_length"]
    verbose_name_plural = "oxen"

class Ox(models.Model):
    Meta = _Meta
    horn_length = models.IntegerField()
  • 使用“嵌套类”方法,可以读取嵌套的项目符号点列表,但是使用“引用内部类”方法,则必须向上滚动以查看其定义,_Meta以查看其“子项”(属性)。

  • 如果您的代码嵌套级别增加或由于其他原因导致行很长,则“引用的内部类”方法可能更具可读性。

*当然是口味问题

3.3略有不同的错误消息

这没什么大不了的,只是为了完整性:当访问内部类的不存在属性时,我们会看到截然不同的异常。继续第2节中给出的示例:

innercls1.foo()
# AttributeError: type object 'Inner' has no attribute 'foo'

innercls2.foo()
# AttributeError: type object '_InnerClass' has no attribute 'foo'

这是因为type内部类的s是

type(innercls1())
#mypackage.outer1.MyOuter1.Inner

type(innercls2())
#mypackage.outer2._InnerClass

1. Two functionally equivalent ways

The two ways shown before are functionally identical. However, there are some subtle differences, and there are situations when you would like to choose one over another.

Way 1: Nested class definition
(=”Nested class”)

class MyOuter1:
    class Inner:
        def show(self, msg):
            print(msg)

Way 2: With module level Inner class attached to Outer class
(=”Referenced inner class”)

class _InnerClass:
    def show(self, msg):
        print(msg)

class MyOuter2:
    Inner = _InnerClass

Underscore is used to follow PEP8 “internal interfaces (packages, modules, classes, functions, attributes or other names) should — be prefixed with a single leading underscore.”

2. Similarities

Below code snippet demonstrates the functional similarities of the “Nested class” vs “Referenced inner class”; They would behave the same way in code checking for the type of an inner class instance. Needless to say, the m.inner.anymethod() would behave similarly with m1 and m2

m1 = MyOuter1()
m2 = MyOuter2()

innercls1 = getattr(m1, 'Inner', None)
innercls2 = getattr(m2, 'Inner', None)

isinstance(innercls1(), MyOuter1.Inner)
# True

isinstance(innercls2(), MyOuter2.Inner)
# True

type(innercls1()) == mypackage.outer1.MyOuter1.Inner
# True (when part of mypackage)

type(innercls2()) == mypackage.outer2.MyOuter2.Inner
# True (when part of mypackage)

3. Differences

The differences of “Nested class” and “Referenced inner class” are listed below. They are not big, but sometimes you would like to choose one or the other based on these.

3.1 Code Encapsulation

With “Nested classes” it is possible to encapsulate code better than with “Referenced inner class”. A class in the module namespace is a global variable. The purpose of nested classes is to reduce clutter in the module and put the inner class inside the outer class.

While no-one* is using from packagename import *, low amount of module level variables can be nice for example when using an IDE with code completion / intellisense.

*Right?

3.2 Readability of code

Django documentation instructs to use inner class Meta for model metadata. It is a bit more clearer* to instruct the framework users to write a class Foo(models.Model) with inner class Meta;

class Ox(models.Model):
    horn_length = models.IntegerField()

    class Meta:
        ordering = ["horn_length"]
        verbose_name_plural = "oxen"

instead of “write a class _Meta, then write a class Foo(models.Model) with Meta = _Meta“;

class _Meta:
    ordering = ["horn_length"]
    verbose_name_plural = "oxen"

class Ox(models.Model):
    Meta = _Meta
    horn_length = models.IntegerField()
  • With the “Nested class” approach the code can be read a nested bullet point list, but with the “Referenced inner class” method one has to scroll back up to see the definition of _Meta to see its “child items” (attributes).

  • The “Referenced inner class” method can be more readable if your code nesting level grows or the rows are long for some other reason.

* Of course, a matter of taste

3.3 Slightly different error messages

This is not a big deal, but just for completeness: When accessing non-existent attribute for the inner class, we see slighly different exceptions. Continuing the example given in Section 2:

innercls1.foo()
# AttributeError: type object 'Inner' has no attribute 'foo'

innercls2.foo()
# AttributeError: type object '_InnerClass' has no attribute 'foo'

This is because the types of the inner classes are

type(innercls1())
#mypackage.outer1.MyOuter1.Inner

type(innercls2())
#mypackage.outer2._InnerClass

回答 8

我已经使用Python的内部类在unittest函数(即内部def test_something():)中故意创建了错误的子类,以便接近100%的测试覆盖率(例如,通过覆盖某些方法来测试很少触发的日志语句)。

回想起来,它类似于埃德的答案https://stackoverflow.com/a/722036/1101109

一旦删除了所有内部引用,此类内部类便超出范围,并准备进行垃圾回收。例如,获取以下inner.py文件:

class A(object):
    pass

def scope():
    class Buggy(A):
        """Do tests or something"""
    assert isinstance(Buggy(), A)

在OSX Python 2.7.6下得到以下奇怪结果:

>>> from inner import A, scope
>>> A.__subclasses__()
[]
>>> scope()
>>> A.__subclasses__()
[<class 'inner.Buggy'>]
>>> del A, scope
>>> from inner import A
>>> A.__subclasses__()
[<class 'inner.Buggy'>]
>>> del A
>>> import gc
>>> gc.collect()
0
>>> gc.collect()  # Yes I needed to call the gc twice, seems reproducible
3
>>> from inner import A
>>> A.__subclasses__()
[]

提示-不要继续尝试使用Django模型,这似乎保留了其他(已缓存?)对我的越野车类的引用。

因此,总的来说,我不建议您将内部类用于此类目的,除非您确实确实认为100%的测试覆盖率并且不能使用其他方法。虽然我觉得很高兴知道,如果使用__subclasses__(),它有时会被内部类污染。无论哪种方式,如果您走了这么远,我都认为到目前为止,我们对Python,私有dunderscores以及所有内容都非常了解。

I have used Python’s inner classes to create deliberately buggy subclasses within unittest functions (i.e. inside def test_something():) in order to get closer to 100% test coverage (e.g. testing very rarely triggered logging statements by overriding some methods).

In retrospect it’s similar to Ed’s answer https://stackoverflow.com/a/722036/1101109

Such inner classes should go out of scope and be ready for garbage collection once all references to them have been removed. For instance, take the following inner.py file:

class A(object):
    pass

def scope():
    class Buggy(A):
        """Do tests or something"""
    assert isinstance(Buggy(), A)

I get the following curious results under OSX Python 2.7.6:

>>> from inner import A, scope
>>> A.__subclasses__()
[]
>>> scope()
>>> A.__subclasses__()
[<class 'inner.Buggy'>]
>>> del A, scope
>>> from inner import A
>>> A.__subclasses__()
[<class 'inner.Buggy'>]
>>> del A
>>> import gc
>>> gc.collect()
0
>>> gc.collect()  # Yes I needed to call the gc twice, seems reproducible
3
>>> from inner import A
>>> A.__subclasses__()
[]

Hint – Don’t go on and try doing this with Django models, which seemed to keep other (cached?) references to my buggy classes.

So in general, I wouldn’t recommend using inner classes for this kind of purpose unless you really do value that 100% test coverage and can’t use other methods. Though I think it’s nice to be aware that if you use the __subclasses__(), that it can sometimes get polluted by inner classes. Either way if you followed this far, I think we’re pretty deep into Python at this point, private dunderscores and all.


如何在Python的类方法中访问“静态”类变量?

问题:如何在Python的类方法中访问“静态”类变量?

如果我有以下python代码:

class Foo(object):
    bar = 1

    def bah(self):
        print(bar)

f = Foo()
f.bah()

它抱怨

NameError: global name 'bar' is not defined

如何bar在方法中访问类/静态变量bah

If I have the following python code:

class Foo(object):
    bar = 1

    def bah(self):
        print(bar)

f = Foo()
f.bah()

It complains

NameError: global name 'bar' is not defined

How can I access class/static variable bar within method bah?


回答 0

代替bar使用self.barFoo.bar。分配给Foo.bar将创建一个静态变量,分配给self.bar将创建一个实例变量。

Instead of bar use self.bar or Foo.bar. Assigning to Foo.bar will create a static variable, and assigning to self.bar will create an instance variable.


回答 1

定义类方法:

class Foo(object):
    bar = 1
    @classmethod
    def bah(cls):    
        print cls.bar

现在,如果bah()必须是实例方法(即可以访问self),则仍可以直接访问类变量。

class Foo(object):
    bar = 1
    def bah(self):    
        print self.bar

Define class method:

class Foo(object):
    bar = 1
    @classmethod
    def bah(cls):    
        print cls.bar

Now if bah() has to be instance method (i.e. have access to self), you can still directly access the class variable.

class Foo(object):
    bar = 1
    def bah(self):    
        print self.bar

回答 2

与所有好的示例一样,您已经简化了实际要执行的操作。这很好,但是值得注意的是,在类变量和实例变量之间,python具有很大的灵活性。方法也可以这样说。有关各种可能性的建议,我建议阅读MichaelFötsch的新型类介绍,尤其是第2至6节。

入门时需要记住很多事情的一件事是python不是java。 不只是陈词滥调。在Java中,整个类都会被编译,使命名空间解析真正简单:方法外(在任何地方)声明的任何变量都是实例(或类,如果是静态的话)变量,并且可以在方法内隐式访问。

使用python的主要经验法则是,按顺序搜索三个命名空间以查找变量:

  1. 功能/方法
  2. 当前模块
  3. 内建

{begin pedagogy}

有一些exceptions。我主要想到的是,在加载类定义时,该类定义是其自己的隐式命名空间。但这仅在模块被加载时才持续,并且在方法内时将被完全忽略。从而:

>>> class A(object):
        foo = 'foo'
        bar = foo


>>> A.foo
'foo'
>>> A.bar
'foo'

但:

>>> class B(object):
        foo = 'foo'
        def get_foo():
            return foo
        bar = get_foo()



Traceback (most recent call last):
  File "<pyshell#11>", line 1, in <module>
    class B(object):
  File "<pyshell#11>", line 5, in B
    bar = get_foo()
  File "<pyshell#11>", line 4, in get_foo
    return foo
NameError: global name 'foo' is not defined

{end pedagogy}

最后,要记住的是,您确实有权访问要访问的任何变量,但可能不是隐式的。如果您的目标简单明了,那么选择Foo.bar或self.bar可能就足够了。如果您的示例变得越来越复杂,或者您想做一些继承之类的事情(可以继承静态/类方法!),或者在类本身中引用类名的想法对您来说是错误的,请查看我链接的简介。

As with all good examples, you’ve simplified what you’re actually trying to do. This is good, but it is worth noting that python has a lot of flexibility when it comes to class versus instance variables. The same can be said of methods. For a good list of possibilities, I recommend reading Michael Fötsch’ new-style classes introduction, especially sections 2 through 6.

One thing that takes a lot of work to remember when getting started is that python is not java. More than just a cliche. In java, an entire class is compiled, making the namespace resolution real simple: any variables declared outside a method (anywhere) are instance (or, if static, class) variables and are implicitly accessible within methods.

With python, the grand rule of thumb is that there are three namespaces that are searched, in order, for variables:

  1. The function/method
  2. The current module
  3. Builtins

{begin pedagogy}

There are limited exceptions to this. The main one that occurs to me is that, when a class definition is being loaded, the class definition is its own implicit namespace. But this lasts only as long as the module is being loaded, and is entirely bypassed when within a method. Thus:

>>> class A(object):
        foo = 'foo'
        bar = foo


>>> A.foo
'foo'
>>> A.bar
'foo'

but:

>>> class B(object):
        foo = 'foo'
        def get_foo():
            return foo
        bar = get_foo()



Traceback (most recent call last):
  File "<pyshell#11>", line 1, in <module>
    class B(object):
  File "<pyshell#11>", line 5, in B
    bar = get_foo()
  File "<pyshell#11>", line 4, in get_foo
    return foo
NameError: global name 'foo' is not defined

{end pedagogy}

In the end, the thing to remember is that you do have access to any of the variables you want to access, but probably not implicitly. If your goals are simple and straightforward, then going for Foo.bar or self.bar will probably be sufficient. If your example is getting more complicated, or you want to do fancy things like inheritance (you can inherit static/class methods!), or the idea of referring to the name of your class within the class itself seems wrong to you, check out the intro I linked.


回答 3

class Foo(object):
     bar = 1
     def bah(self):
         print Foo.bar

f = Foo() 
f.bah()
class Foo(object):
     bar = 1
     def bah(self):
         print Foo.bar

f = Foo() 
f.bah()

回答 4

class Foo(object):    
    bar = 1

    def bah(object_reference):
        object_reference.var = Foo.bar
        return object_reference.var


f = Foo() 
print 'var=', f.bah()
class Foo(object):    
    bar = 1

    def bah(object_reference):
        object_reference.var = Foo.bar
        return object_reference.var


f = Foo() 
print 'var=', f.bah()

遍历python中的对象属性

问题:遍历python中的对象属性

我有一个带有几个属性和方法的python对象。我想遍历对象属性。

class my_python_obj(object):
    attr1='a'
    attr2='b'
    attr3='c'

    def method1(self, etc, etc):
        #Statements

我想生成一个包含所有对象属性及其当前值的字典,但是我想以一种动态的方式进行操作(因此,如果以后添加另一个属性,我也不必记住要更新我的函数)。

在php中,变量可以用作键,但是python中的对象是不可写的,如果我为此使用点符号,则会创建一个新属性,其名称为var,这不是我的意图。

为了使事情更清楚:

def to_dict(self):
    '''this is what I already have'''
    d={}
    d["attr1"]= self.attr1
    d["attr2"]= self.attr2
    d["attr3"]= self.attr3
    return d

·

def to_dict(self):
    '''this is what I want to do'''
    d={}
    for v in my_python_obj.attributes:
        d[v] = self.v
    return d

更新:对于属性,我的意思是仅此对象的变量,而不是方法。

I have a python object with several attributes and methods. I want to iterate over object attributes.

class my_python_obj(object):
    attr1='a'
    attr2='b'
    attr3='c'

    def method1(self, etc, etc):
        #Statements

I want to generate a dictionary containing all of the objects attributes and their current values, but I want to do it in a dynamic way (so if later I add another attribute I don’t have to remember to update my function as well).

In php variables can be used as keys, but objects in python are unsuscriptable and if I use the dot notation for this it creates a new attribute with the name of my var, which is not my intent.

Just to make things clearer:

def to_dict(self):
    '''this is what I already have'''
    d={}
    d["attr1"]= self.attr1
    d["attr2"]= self.attr2
    d["attr3"]= self.attr3
    return d

·

def to_dict(self):
    '''this is what I want to do'''
    d={}
    for v in my_python_obj.attributes:
        d[v] = self.v
    return d

Update: With attributes I mean only the variables of this object, not the methods.


回答 0

假设您有一个诸如

>>> class Cls(object):
...     foo = 1
...     bar = 'hello'
...     def func(self):
...         return 'call me'
...
>>> obj = Cls()

调用dir该对象会返回该对象的所有属性,包括python特殊属性。尽管某些对象属性是可调用的,例如方法。

>>> dir(obj)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bar', 'foo', 'func']

您始终可以使用列表理解来过滤掉特殊方法。

>>> [a for a in dir(obj) if not a.startswith('__')]
['bar', 'foo', 'func']

或者您更喜欢地图/过滤器。

>>> filter(lambda a: not a.startswith('__'), dir(obj))
['bar', 'foo', 'func']

如果要过滤掉这些方法,可以使用内置函数callable作为检查。

>>> [a for a in dir(obj) if not a.startswith('__') and not callable(getattr(obj, a))]
['bar', 'foo']

您还可以使用检查类及其实例对象之间的差异。

>>> set(dir(Cls)) - set(dir(object))
set(['__module__', 'bar', 'func', '__dict__', 'foo', '__weakref__'])

Assuming you have a class such as

>>> class Cls(object):
...     foo = 1
...     bar = 'hello'
...     def func(self):
...         return 'call me'
...
>>> obj = Cls()

calling dir on the object gives you back all the attributes of that object, including python special attributes. Although some object attributes are callable, such as methods.

>>> dir(obj)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bar', 'foo', 'func']

You can always filter out the special methods by using a list comprehension.

>>> [a for a in dir(obj) if not a.startswith('__')]
['bar', 'foo', 'func']

or if you prefer map/filters.

>>> filter(lambda a: not a.startswith('__'), dir(obj))
['bar', 'foo', 'func']

If you want to filter out the methods, you can use the builtin callable as a check.

>>> [a for a in dir(obj) if not a.startswith('__') and not callable(getattr(obj, a))]
['bar', 'foo']

You could also inspect the difference between your class and its instance object using.

>>> set(dir(Cls)) - set(dir(object))
set(['__module__', 'bar', 'func', '__dict__', 'foo', '__weakref__'])

回答 1

通常,__iter__在您的类中放置一个方法并遍历对象属性,或者将此mixin类放入您的类中。

class IterMixin(object):
    def __iter__(self):
        for attr, value in self.__dict__.iteritems():
            yield attr, value

你的班:

>>> class YourClass(IterMixin): pass
...
>>> yc = YourClass()
>>> yc.one = range(15)
>>> yc.two = 'test'
>>> dict(yc)
{'one': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], 'two': 'test'}

in general put a __iter__ method in your class and iterate through the object attributes or put this mixin class in your class.

class IterMixin(object):
    def __iter__(self):
        for attr, value in self.__dict__.iteritems():
            yield attr, value

Your class:

>>> class YourClass(IterMixin): pass
...
>>> yc = YourClass()
>>> yc.one = range(15)
>>> yc.two = 'test'
>>> dict(yc)
{'one': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], 'two': 'test'}

回答 2

正如已经在一些答案/评论中提到的那样,Python对象已经存储了其属性的字典(不包括方法)。可以通过进行访问__dict__,但是更好的方法是使用vars(尽管输出是相同的)。请注意,修改此字典将修改实例的属性!这可能很有用,但也意味着您应该谨慎使用此字典。这是一个简单的例子:

class A():
    def __init__(self, x=3, y=2, z=5):
        self.x = x
        self._y = y
        self.__z__ = z

    def f(self):
        pass

a = A()
print(vars(a))
# {'x': 3, '_y': 2, '__z__': 5}
# all of the attributes of `a` but no methods!

# note how the dictionary is always up-to-date
a.x = 10
print(vars(a))
# {'x': 10, '_y': 2, '__z__': 5}

# modifying the dictionary modifies the instance attribute
vars(a)["_y"] = 20
print(vars(a))
# {'x': 10, '_y': 20, '__z__': 5}

使用这个dir(a)方法很奇怪,即使不是完全不好,也可以解决这个问题。如果您确实需要遍历该类的所有属性和方法(包括诸如的特殊方法__init__),那是很好的。但是,这似乎不是您想要的,甚至不是您所接受的答案通过使用一些易碎的过滤来尝试删除方法并仅保留属性。您会看到A上面定义的类将如何失败。

__dict__已经在几个答案中使用,但是它们都定义了不必要的方法,而不是直接使用它。只有注释建议使用vars)。

As mentioned in some of the answers/comments already, Python objects already store a dictionary of their attributes (methods aren’t included). This can be accessed as __dict__, but the better way is to use vars (the output is the same, though). Note that modifying this dictionary will modify the attributes on the instance! This can be useful, but also means you should be careful with how you use this dictionary. Here’s a quick example:

class A():
    def __init__(self, x=3, y=2, z=5):
        self.x = x
        self._y = y
        self.__z__ = z

    def f(self):
        pass

a = A()
print(vars(a))
# {'x': 3, '_y': 2, '__z__': 5}
# all of the attributes of `a` but no methods!

# note how the dictionary is always up-to-date
a.x = 10
print(vars(a))
# {'x': 10, '_y': 2, '__z__': 5}

# modifying the dictionary modifies the instance attribute
vars(a)["_y"] = 20
print(vars(a))
# {'x': 10, '_y': 20, '__z__': 5}

Using dir(a) is an odd, if not outright bad, approach to this problem. It’s good if you really needed to iterate over all attributes and methods of the class (including the special methods like __init__). However, this doesn’t seem to be what you want, and even the accepted answer goes about this poorly by applying some brittle filtering to try to remove methods and leave just the attributes; you can see how this would fail for the class A defined above.

(using __dict__ has been done in a couple of answers, but they all define unnecessary methods instead of using it directly. Only a comment suggests to use vars).


回答 3

python中的对象将它们的属性(包括函数)存储在称为的字典中__dict__。您可以(但通常不应该)使用它直接访问属性。如果您只想要一个列表,也可以调用dir(obj),它返回带有所有属性名称的Iterable,然后可以将其传递给getattr

但是,需要对变量名称做任何事情通常都是不好的设计。为什么不将它们保存在集合中?

class Foo(object):
    def __init__(self, **values):
        self.special_values = values

然后,您可以使用 for key in obj.special_values:

Objects in python store their atributes (including functions) in a dict called __dict__. You can (but generally shouldn’t) use this to access the attributes directly. If you just want a list, you can also call dir(obj), which returns an iterable with all the attribute names, which you could then pass to getattr.

However, needing to do anything with the names of the variables is usually bad design. Why not keep them in a collection?

class Foo(object):
    def __init__(self, **values):
        self.special_values = values

You can then iterate over the keys with for key in obj.special_values:


回答 4

class someclass:
        x=1
        y=2
        z=3
        def __init__(self):
           self.current_idx = 0
           self.items = ["x","y","z"]
        def next(self):
            if self.current_idx < len(self.items):
                self.current_idx += 1
                k = self.items[self.current_idx-1]
                return (k,getattr(self,k))
            else:
                raise StopIteration
        def __iter__(self):
           return self

然后称之为可迭代

s=someclass()
for k,v in s:
    print k,"=",v
class someclass:
        x=1
        y=2
        z=3
        def __init__(self):
           self.current_idx = 0
           self.items = ["x","y","z"]
        def next(self):
            if self.current_idx < len(self.items):
                self.current_idx += 1
                k = self.items[self.current_idx-1]
                return (k,getattr(self,k))
            else:
                raise StopIteration
        def __iter__(self):
           return self

then just call it as an iterable

s=someclass()
for k,v in s:
    print k,"=",v

回答 5

正确的答案是您不应该这样做。如果您想要这种类型的东西,要么只使用dict,要么需要将属性显式添加到某个容器中。您可以通过了解装饰器来实现自动化。

尤其是,顺便说一句,示例中的method1同样具有属性。

The correct answer to this is that you shouldn’t. If you want this type of thing either just use a dict, or you’ll need to explicitly add attributes to some container. You can automate that by learning about decorators.

In particular, by the way, method1 in your example is just as good of an attribute.


回答 6

对于python 3.6

class SomeClass:

    def attr_list(self, should_print=False):

        items = self.__dict__.items()
        if should_print:
            [print(f"attribute: {k}    value: {v}") for k, v in items]

        return items

For python 3.6

class SomeClass:

    def attr_list(self, should_print=False):

        items = self.__dict__.items()
        if should_print:
            [print(f"attribute: {k}    value: {v}") for k, v in items]

        return items

回答 7

对于所有的Python狂热分子,我相信Johan Cleeze会赞成您的教条主义;)。我要离开这个答案,继续贬低它,这实际上使我更加知己。留下你的评论!

对于python 3.6

class SomeClass:

    def attr_list1(self, should_print=False):

        for k in self.__dict__.keys():
            v = self.__dict__.__getitem__(k)
            if should_print:
                print(f"attr: {k}    value: {v}")

    def attr_list(self, should_print=False):

        b = [(k, v) for k, v in self.__dict__.items()]
        if should_print:
            [print(f"attr: {a[0]}    value: {a[1]}") for a in b]
        return b

For all the pythonian zealots out there I’m sure Johan Cleeze would approve of your dogmatism ;). I’m leaving this answer keep demeriting it It actually makes me more confidant. Leave a comment you chickens!

For python 3.6

class SomeClass:

    def attr_list1(self, should_print=False):

        for k in self.__dict__.keys():
            v = self.__dict__.__getitem__(k)
            if should_print:
                print(f"attr: {k}    value: {v}")

    def attr_list(self, should_print=False):

        b = [(k, v) for k, v in self.__dict__.items()]
        if should_print:
            [print(f"attr: {a[0]}    value: {a[1]}") for a in b]
        return b

如何在Python中设计类?

问题:如何在Python中设计类?

我在以前的问题中为检测爪子中的爪子脚趾提供了非常出色的帮助,但是所有这些解决方案一次只能进行一次测量。

现在,我得到的数据包括:

  • 大约30条狗;
  • 每个都有24个测量值(分为几个子组);
  • 每次测量至少有4个接触点(每只爪子一个),并且
    • 每个联系人分为5部分,
    • 具有几个参数,例如接触时间,位置,总力等。

显然,将所有内容粘贴到一个大对象中并不会减少它,因此我认为我需要使用类而不是当前的许多函数。但是,即使我已经阅读了学习Python的有关类的章节,也无法将其应用于我自己的代码(GitHub链接

我也觉得每次我想获取一些信息时处理所有数据都是很奇怪的。一旦知道了每个爪子的位置,就没有理由再进行计算了。此外,我想比较同一只狗的所有爪子,以确定哪个接触属于哪个爪子(前/后,左/右)。如果我继续只使用函数,那将变得一团糟。

因此,现在我正在寻找有关如何创建类的建议,这些类将使我能够以明智的方式处理我的数据(链接到一只狗的压缩数据)。

I’ve had some really awesome help on my previous questions for detecting paws and toes within a paw, but all these solutions only work for one measurement at a time.

Now I have data that consists off:

  • about 30 dogs;
  • each has 24 measurements (divided into several subgroups);
  • each measurement has at least 4 contacts (one for each paw) and
    • each contact is divided into 5 parts and
    • has several parameters, like contact time, location, total force etc.

Obviously sticking everything into one big object isn’t going to cut it, so I figured I needed to use classes instead of the current slew of functions. But even though I’ve read Learning Python’s chapter about classes, I fail to apply it to my own code (GitHub link)

I also feel like it’s rather strange to process all the data every time I want to get out some information. Once I know the locations of each paw, there’s no reason for me to calculate this again. Furthermore, I want to compare all the paws of the same dog to determine which contact belongs to which paw (front/hind, left/right). This would become a mess if I continue using only functions.

So now I’m looking for advice on how to create classes that will let me process my data (link to the zipped data of one dog) in a sensible fashion.


回答 0

如何设计类。

  1. 写下单词。您开始这样做。有些人没有,不知道为什么会有问题。

  2. 将您的词汇集扩展为有关这些对象将要做什么的简单说明。也就是说,写下您将要在这些事情上进行的各种计算。您的30条狗的清单,24条测量值,4个联系人以及每个联系人几个“参数”很有趣,但这只是故事的一部分。您的“每个爪子的位置”和“比较同一只狗的所有爪子,以确定哪个联系人属于哪个爪子”是对象设计的下一步。

  3. 在名词下划线。说真的 一些人参数这种方法的价值,但是我发现对于初次面向对象的开发人员来说,它有所帮助。在名词下划线。

  4. 查看名词。诸如“参数”和“度量”之类的通用名词需要替换为在问题域中适用于您的问题的特定,具体名词。细节有助于澄清问题。泛型只是忽略细节。

  5. 对于每个名词(“接触”,“爪子”,“狗”等),写下该名词的属性以及该对象参与的动作。不要捷径。每个属性。例如,“数据集包含30条狗”很重要。

  6. 对于每个属性,请确定这是与已定义名词的关系,还是与其他类型的“原始”或“原子”数据(如字符串或浮点数或不可约数)的关系。

  7. 对于每个动作或操作,您必须确定哪个名词有责任,哪些名词仅参与其中。这是“可变性”的问题。有些对象得到更新,而另一些则没有。可变对象必须对其突变负全部责任。

  8. 此时,您可以开始将名词转换为类定义。一些集合名词是列表,字典,元组,集合或命名元组,您不需要做很多工作。由于复杂的派生数据或执行的某些更新/变异,其他类则更为复杂。

不要忘记使用unittest单独测试每个类。

另外,没有法律规定Class必须是可变的。例如,就您而言,您几乎没有可变数据。您所拥有的是派生数据,这些数据是通过转换功能从源数据集中创建的。

How to design a class.

  1. Write down the words. You started to do this. Some people don’t and wonder why they have problems.

  2. Expand your set of words into simple statements about what these objects will be doing. That is to say, write down the various calculations you’ll be doing on these things. Your short list of 30 dogs, 24 measurements, 4 contacts, and several “parameters” per contact is interesting, but only part of the story. Your “locations of each paw” and “compare all the paws of the same dog to determine which contact belongs to which paw” are the next step in object design.

  3. Underline the nouns. Seriously. Some folks debate the value of this, but I find that for first-time OO developers it helps. Underline the nouns.

  4. Review the nouns. Generic nouns like “parameter” and “measurement” need to be replaced with specific, concrete nouns that apply to your problem in your problem domain. Specifics help clarify the problem. Generics simply elide details.

  5. For each noun (“contact”, “paw”, “dog”, etc.) write down the attributes of that noun and the actions in which that object engages. Don’t short-cut this. Every attribute. “Data Set contains 30 Dogs” for example is important.

  6. For each attribute, identify if this is a relationship to a defined noun, or some other kind of “primitive” or “atomic” data like a string or a float or something irreducible.

  7. For each action or operation, you have to identify which noun has the responsibility, and which nouns merely participate. It’s a question of “mutability”. Some objects get updated, others don’t. Mutable objects must own total responsibility for their mutations.

  8. At this point, you can start to transform nouns into class definitions. Some collective nouns are lists, dictionaries, tuples, sets or namedtuples, and you don’t need to do very much work. Other classes are more complex, either because of complex derived data or because of some update/mutation which is performed.

Don’t forget to test each class in isolation using unittest.

Also, there’s no law that says classes must be mutable. In your case, for example, you have almost no mutable data. What you have is derived data, created by transformation functions from the source dataset.


回答 1

以下建议(类似于@ S.Lott的建议)来自《Beginning Python:从新手到专业》一书

  1. 写下您的问题的描述(问题应该做什么?)。在所有名词,动词和形容词下划线。

  2. 遍历名词,寻找可能的类别。

  3. 遍历动词,寻找可能的方法。

  4. 浏览形容词,寻找潜在的属性

  5. 将方法和属性分配给您的类

为了完善课堂,本书还建议我们可以执行以下操作:

  1. 写下(或构想)一组用例,以了解如何使用程序。尝试涵盖所有功能。

  2. 逐步思考每个用例,确保涵盖了我们所需的一切。

The following advices (similar to @S.Lott’s advice) are from the book, Beginning Python: From Novice to Professional

  1. Write down a description of your problem (what should the problem do?). Underline all the nouns, verbs, and adjectives.

  2. Go through the nouns, looking for potential classes.

  3. Go through the verbs, looking for potential methods.

  4. Go through the adjectives, looking for potential attributes

  5. Allocate methods and attributes to your classes

To refine the class, the book also advises we can do the following:

  1. Write down (or dream up) a set of use cases—scenarios of how your program may be used. Try to cover all the functionally.

  2. Think through every use case step by step, making sure that everything we need is covered.


回答 2

我喜欢TDD方法…因此,首先针对所需的行为编写测试。并编写通过的代码。在这一点上,不必太担心设计,只需获得通过测试的套件和软件即可。如果您最终遇到一个丑陋的类,并且使用复杂的方法,请不要担心。

有时,在此初始过程中,您会发现难以测试且需要分解的行为(仅出于可测试性)。这可能暗示需要单独的类。

然后是有趣的部分…重构。使用了可用的软件后,您可以看到复杂的部分。通常,很少有行为的迹象,这表明有一个新的类,但如果没有,则只寻找简化代码的方法。提取服务对象和值对象。简化您的方法。

如果您正确使用了git(不是,您正在使用git吗?),则可以在重构过程中非常快速地尝试进行某些特定的分解,然后放弃它,如果它不能简化事情,请还原并返回。

通过首先编写经过测试的工作代码,您应该获得对问题域的深入了解,而这些问题是设计优先方法无法轻易实现的。编写测试和代码使您摆脱“我从哪里开始”的瘫痪。

I like the TDD approach… So start by writing tests for what you want the behaviour to be. And write code that passes. At this point, don’t worry too much about design, just get a test suite and software that passes. Don’t worry if you end up with a single big ugly class, with complex methods.

Sometimes, during this initial process, you’ll find a behaviour that is hard to test and needs to be decomposed, just for testability. This may be a hint that a separate class is warranted.

Then the fun part… refactoring. After you have working software you can see the complex pieces. Often little pockets of behaviour will become apparent, suggesting a new class, but if not, just look for ways to simplify the code. Extract service objects and value objects. Simplify your methods.

If you’re using git properly (you are using git, aren’t you?), you can very quickly experiment with some particular decomposition during refactoring, and then abandon it and revert back if it doesn’t simplify things.

By writing tested working code first you should gain an intimate insight into the problem domain that you couldn’t easily get with the design-first approach. Writing tests and code push you past that “where do I begin” paralysis.


回答 3

OO设计的整个思想是使您的代码映射到您的问题,因此,例如,当您想要一只狗的第一个足迹时,您可以执行以下操作:

dog.footstep(0)

现在,对于您的情况,可能需要读取原始数据文件并计算足迹位置。所有这些都可以隐藏在footstep()函数中,以便仅发生一次。就像是:

 class Dog:
   def __init__(self):
     self._footsteps=None 
   def footstep(self,n):
     if not self._footsteps:
        self.readInFootsteps(...)
     return self._footsteps[n]

[现在这是一种缓存模式。第一次读取足迹数据,随后又从self._footsteps获取。]

但是,是的,正确设计OO设计可能很棘手。多想想您要对数据执行的操作,这将告诉您将什么方法应用于什么类。

The whole idea of OO design is to make your code map to your problem, so when, for example, you want the first footstep of a dog, you do something like:

dog.footstep(0)

Now, it may be that for your case you need to read in your raw data file and compute the footstep locations. All this could be hidden in the footstep() function so that it only happens once. Something like:

 class Dog:
   def __init__(self):
     self._footsteps=None 
   def footstep(self,n):
     if not self._footsteps:
        self.readInFootsteps(...)
     return self._footsteps[n]

[This is now a sort of caching pattern. The first time it goes and reads the footstep data, subsequent times it just gets it from self._footsteps.]

But yes, getting OO design right can be tricky. Think more about the things you want to do to your data, and that will inform what methods you’ll need to apply to what classes.


回答 4

写出您的名词,动词,形容词是一种很好的方法,但是我更倾向于将类设计看作是询问应该隐藏哪些数据的问题?

假设您有一个Query对象和一个Database对象:

Query对象将帮助您创建和存储查询-存储是此处的关键,因为函数可以帮助您轻松创建一个查询。也许您可以留下:Query().select('Country').from_table('User').where('Country == "Brazil"')。语法无关紧要-这就是您的工作!-关键是对象可以帮助您隐藏某些东西,在这种情况下,是存储和输出查询所必需的数据。对象的强大功能来自使用它的语法(在这种情况下,是一种巧妙的链接),并且不需要知道它存储了什么才能使其工作。如果操作正确,则该Query对象可以输出对多个数据库的查询。它在内部将存储特定格式,但在输出时可以轻松转换为其他格式(Postgres,MySQL,MongoDB)。

现在让我们仔细考虑一下Database对象。这个藏起来什么?显然,它不能存储数据库的全部内容,因为这就是我们拥有数据库的原因!那有什么意义呢?目的是向使用对象的人员隐藏数据库的工作方式Database。好的类将在处理内部状态时简化推理。对于此Database对象,您可以隐藏网络调用的工作方式,或批处理查询或更新,或提供缓存层。

问题是这个Database对象很大。它代表了如何访问数据库,因此它可以做任何事情。显然,根据系统的不同,很难进行联网,缓存和批处理,因此将它们隐藏起来将非常有帮助。但是,正如许多人会注意到的那样,数据库异常复杂,而且与原始DB调用之间的距离越远,调整性能和理解事情的工作就越困难。

这是OOP的基本权衡。如果选择正确的抽象,它会使编码更简单(字符串,数组,字典),如果选择的抽象太大(数据库,EmailManager,NetworkingManager),则可能变得太复杂而无法真正了解其工作原理或如何处理。期望。目的是隐藏复杂性,但是一定要复杂。一个好的经验法则是从避免Manager对象开始,而是创建类似的类structs-它们所做的只是保存数据,并使用一些辅助方法来创建/处理数据,从而使您的生活更轻松。例如,在以EmailManager调用sendEmail一个Email对象的函数开始的情况下。这是一个简单的起点,并且代码很容易理解。

对于您的示例,请考虑需要将哪些数据组合在一起以计算所需的内容。例如,如果您想知道一只动物走了多远,您可以拥有AnimalStepAnimalTrip(收集AnimalSteps)类。既然每个Trip都具有所有Step数据,那么它应该能够弄清楚它的内容,也许AnimalTrip.calculateDistance()是有道理的。

Writing out your nouns, verbs, adjectives is a great approach, but I prefer to think of class design as asking the question what data should be hidden?

Imagine you had a Query object and a Database object:

The Query object will help you create and store a query — store, is the key here, as a function could help you create one just as easily. Maybe you could stay: Query().select('Country').from_table('User').where('Country == "Brazil"'). It doesn’t matter exactly the syntax — that is your job! — the key is the object is helping you hide something, in this case the data necessary to store and output a query. The power of the object comes from the syntax of using it (in this case some clever chaining) and not needing to know what it stores to make it work. If done right the Query object could output queries for more then one database. It internally would store a specific format but could easily convert to other formats when outputting (Postgres, MySQL, MongoDB).

Now let’s think through the Database object. What does this hide and store? Well clearly it can’t store the full contents of the database, since that is why we have a database! So what is the point? The goal is to hide how the database works from people who use the Database object. Good classes will simplify reasoning when manipulating internal state. For this Database object you could hide how the networking calls work, or batch queries or updates, or provide a caching layer.

The problem is this Database object is HUGE. It represents how to access a database, so under the covers it could do anything and everything. Clearly networking, caching, and batching are quite hard to deal with depending on your system, so hiding them away would be very helpful. But, as many people will note, a database is insanely complex, and the further from the raw DB calls you get, the harder it is to tune for performance and understand how things work.

This is the fundamental tradeoff of OOP. If you pick the right abstraction it makes coding simpler (String, Array, Dictionary), if you pick an abstraction that is too big (Database, EmailManager, NetworkingManager), it may become too complex to really understand how it works, or what to expect. The goal is to hide complexity, but some complexity is necessary. A good rule of thumb is to start out avoiding Manager objects, and instead create classes that are like structs — all they do is hold data, with some helper methods to create/manipulate the data to make your life easier. For example, in the case of EmailManager start with a function called sendEmail that takes an Email object. This is a simple starting point and the code is very easy to understand.

As for your example, think about what data needs to be together to calculate what you are looking for. If you wanted to know how far an animal was walking, for example, you could have AnimalStep and AnimalTrip (collection of AnimalSteps) classes. Now that each Trip has all the Step data, then it should be able to figure stuff out about it, perhaps AnimalTrip.calculateDistance() makes sense.


回答 5

浏览了链接的代码后,在我看来,最好不要在此时设计Dog类。相反,您应该使用Pandasdataframes。数据框是带有列的表。您数据帧都会有这样的栏目:dog_idcontact_partcontact_timecontact_location,等大熊猫在后台使用numpy的阵列,它已经为你许多方便的方法:

  • 通过例如选择一只狗: my_measurements['dog_id']=='Charly'
  • 保存数据: my_measurements.save('filename.pickle')
  • 考虑使用pandas.read_csv()而不是手动读取文本文件。

After skimming your linked code, it seems to me that you are better off not designing a Dog class at this point. Rather, you should use Pandas and dataframes. A dataframe is a table with columns. You dataframe would have columns such as: dog_id, contact_part, contact_time, contact_location, etc. Pandas uses Numpy arrays behind the scenes, and it has many convenience methods for you:

  • Select a dog by e.g. : my_measurements['dog_id']=='Charly'
  • save the data: my_measurements.save('filename.pickle')
  • Consider using pandas.read_csv() instead of manually reading the text files.

关于如何在python中使用属性功能的真实示例?

问题:关于如何在python中使用属性功能的真实示例?

我对如何@property在Python中使用感兴趣。我阅读了python文档,并认为其中的示例只是一个玩具代码:

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

我不知道通过包装_x用属性装饰器填充的内容可以获得什么好处。为什么不只是实现为:

class C(object):
    def __init__(self):
        self.x = None

我认为,属性功能在某些情况下可能很有用。但当?有人可以给我一些真实的例子吗?

谢谢。

I am interested in how to use @property in Python. I’ve read the python docs and the example there, in my opinion, is just a toy code:

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

I do not know what benefit(s) I can get from wrapping the _x filled with the property decorator. Why not just implement as:

class C(object):
    def __init__(self):
        self.x = None

I think, the property feature might be useful in some situations. But when? Could someone please give me some real-world examples?

Thanks.


回答 0

其他示例是对设置的属性进行验证/过滤(强制将它们限制或接受)以及对复杂或快速变化的术语进行惰性评估。

隐藏在属性后面的复杂计算:

class PDB_Calculator(object):
    ...
    @property
    def protein_folding_angle(self):
        # number crunching, remote server calls, etc
        # all results in an angle set in 'some_angle'
        # It could also reference a cache, remote or otherwise,
        # that holds the latest value for this angle
        return some_angle

>>> f = PDB_Calculator()
>>> angle = f.protein_folding_angle
>>> angle
44.33276

验证:

class Pedometer(object)
    ...
    @property
    def stride_length(self):
        return self._stride_length

    @stride_length.setter
    def stride_length(self, value):
        if value > 10:
            raise ValueError("This pedometer is based on the human stride - a stride length above 10m is not supported")
        else:
            self._stride_length = value

Other examples would be validation/filtering of the set attributes (forcing them to be in bounds or acceptable) and lazy evaluation of complex or rapidly changing terms.

Complex calculation hidden behind an attribute:

class PDB_Calculator(object):
    ...
    @property
    def protein_folding_angle(self):
        # number crunching, remote server calls, etc
        # all results in an angle set in 'some_angle'
        # It could also reference a cache, remote or otherwise,
        # that holds the latest value for this angle
        return some_angle

>>> f = PDB_Calculator()
>>> angle = f.protein_folding_angle
>>> angle
44.33276

Validation:

class Pedometer(object)
    ...
    @property
    def stride_length(self):
        return self._stride_length

    @stride_length.setter
    def stride_length(self, value):
        if value > 10:
            raise ValueError("This pedometer is based on the human stride - a stride length above 10m is not supported")
        else:
            self._stride_length = value

回答 1

一个简单的用例是设置一个只读实例属性,因为您知道_x在python 中用一个下划线开头一个变量名通常意味着它是私有的(内部使用),但是有时我们希望能够读取实例属性而不是编写所以我们可以使用property它:

>>> class C(object):

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

        @property
        def x(self):
            return self._x

>>> c = C(1)
>>> c.x
1
>>> c.x = 2
AttributeError        Traceback (most recent call last)

AttributeError: can't set attribute

One simple use case will be to set a read only instance attribute , as you know leading a variable name with one underscore _x in python usually mean it’s private (internal use) but sometimes we want to be able to read the instance attribute and not to write it so we can use property for this:

>>> class C(object):

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

        @property
        def x(self):
            return self._x

>>> c = C(1)
>>> c.x
1
>>> c.x = 2
AttributeError        Traceback (most recent call last)

AttributeError: can't set attribute

回答 2

看一下这篇文章,将其用于非常实际的用途。简而言之,它解释了在Python中通常如何放弃显式的getter / setter方法,因为如果在某个阶段需要它们,则可以使用它property来实现无缝实现。

Take a look at this article for a very practical use. In short, it explains how in Python you can usually ditch explicit getter/setter method, since if you come to need them at some stage you can use property for a seamless implementation.


回答 3

我使用它的一件事是缓存存储在数据库中的查找缓慢但不变的值。这会泛化到您的属性需要计算或您只想按需执行的其他长时间操作(例如数据库检查,网络通信)的任何情况。

class Model(object):

  def get_a(self):
    if not hasattr(self, "_a"):
      self._a = self.db.lookup("a")
    return self._a

  a = property(get_a)

这是在一个Web应用程序中,其中任何给定的页面视图可能只需要一个这样的特定属性,但是基础对象本身可能具有几个这样的属性-在构造时将它们全部初始化将是浪费的,而属性使我可以灵活地使用属性是惰性的,而不是。

One thing I’ve used it for is caching slow-to-look-up, but unchanging, values stored in a database. This generalises to any situation where your attributes require computation or some other long operation (eg. database check, network communication) which you only want to do on demand.

class Model(object):

  def get_a(self):
    if not hasattr(self, "_a"):
      self._a = self.db.lookup("a")
    return self._a

  a = property(get_a)

This was in a web app where any given page view might only need one particular attribute of this kind, but the underlying objects themselves might have several such attributes – initialising them all on construction would be wasteful, and properties allow me to be flexible in which attributes are lazy and which aren’t.


回答 4

通读答案和评论,主题似乎是答案似乎缺少一个简单但有用的示例。我在这里包括了一个非常简单的示例,演示了@property装饰器的简单用法。该类允许用户使用各种不同的单位(即in_feet或)指定并获取距离测量值in_metres

class Distance(object):
    def __init__(self):
        # This private attribute will store the distance in metres
        # All units provided using setters will be converted before
        # being stored
        self._distance = 0.0

    @property
    def in_metres(self):
        return self._distance

    @in_metres.setter
    def in_metres(self, val):
        try:
            self._distance = float(val)
        except:
            raise ValueError("The input you have provided is not recognised "
                             "as a valid number")

    @property
    def in_feet(self):
        return self._distance * 3.2808399

    @in_feet.setter
    def in_feet(self, val):
        try:
            self._distance = float(val) / 3.2808399
        except:
            raise ValueError("The input you have provided is not recognised "
                             "as a valid number")

    @property
    def in_parsecs(self):
        return self._distance * 3.24078e-17

    @in_parsecs.setter
    def in_parsecs(self, val):
        try:
            self._distance = float(val) / 3.24078e-17
        except:
            raise ValueError("The input you have provided is not recognised "
                             "as a valid number")

用法:

>>> distance = Distance()
>>> distance.in_metres = 1000.0
>>> distance.in_metres
1000.0
>>> distance.in_feet
3280.8399
>>> distance.in_parsecs
3.24078e-14

Reading through the answers and comments, the main theme seems to be the answers seem to be missing a simple, yet useful example. I have included a very simple one here that demonstrates the simple use of the @property decorator. It’s a class that allows a user to specify and get distance measurement using a variety of different units, i.e. in_feet or in_metres.

class Distance(object):
    def __init__(self):
        # This private attribute will store the distance in metres
        # All units provided using setters will be converted before
        # being stored
        self._distance = 0.0

    @property
    def in_metres(self):
        return self._distance

    @in_metres.setter
    def in_metres(self, val):
        try:
            self._distance = float(val)
        except:
            raise ValueError("The input you have provided is not recognised "
                             "as a valid number")

    @property
    def in_feet(self):
        return self._distance * 3.2808399

    @in_feet.setter
    def in_feet(self, val):
        try:
            self._distance = float(val) / 3.2808399
        except:
            raise ValueError("The input you have provided is not recognised "
                             "as a valid number")

    @property
    def in_parsecs(self):
        return self._distance * 3.24078e-17

    @in_parsecs.setter
    def in_parsecs(self, val):
        try:
            self._distance = float(val) / 3.24078e-17
        except:
            raise ValueError("The input you have provided is not recognised "
                             "as a valid number")

Usage:

>>> distance = Distance()
>>> distance.in_metres = 1000.0
>>> distance.in_metres
1000.0
>>> distance.in_feet
3280.8399
>>> distance.in_parsecs
3.24078e-14

回答 5

属性只是字段的抽象,它使您可以更好地控制特定字段的操作方式和中间件计算。想到的用法很少是验证,事先初始化和访问限制

@property
def x(self):
    """I'm the 'x' property."""
    if self._x is None:
        self._x = Foo()

    return self._x

Property is just an abstraction around a field which give you more control on ways that a specific field can be manipulated and to do middleware computations. Few of the usages that come to mind is validation and prior initialization and access restriction

@property
def x(self):
    """I'm the 'x' property."""
    if self._x is None:
        self._x = Foo()

    return self._x

回答 6

是的,对于发布的原始示例,该属性的作用与仅具有实例变量“ x”的作用完全相同。

这是关于python属性的最好的事情。从外部看,它们的工作方式完全类似于实例变量!这允许您从类外部使用实例变量。

这意味着您的第一个示例实际上可以使用实例变量。如果情况发生了变化,然后您决定更改实现,并且一个属性很有用,则该类的代码与该类外部的代码的接口将相同。 从实例变量更改为属性不会影响类外部的代码。

许多其他语言和编程类将指示程序员不要公开实例变量,而应使用“ getters”和“ setters”从类外部访问任何值,即使是问题中引用的简单情况也是如此。

类外的代码使用多种语言(例如Java)

object.get_i()
    #and
object.set_i(value)

#in place of (with python)
object.i
    #and 
object.i = value

在实现该类时,有许多“ getter”和“ setter”的作用与您的第一个示例完全相同:复制一个简单的实例变量。这些获取器和设置器是必需的,因为如果类实现更改,则该类外部的所有代码都需要更改。但是python属性允许类外的代码与实例变量相同。因此,如果添加属性或具有简单的实例变量,则无需更改类外部的代码。因此,与大多数面向对象的语言不同,对于您的简单示例,您可以使用实例变量代替真正不需要的“ getters”和“ setters”,因此请确保在将来更改为属性时,可以使用您的Class无需更改。

这意味着仅当行为复杂时才需要创建属性,对于非常普遍的简单情况(如问题中所述),仅需要一个简单的实例变量,您只需使用实例变量即可。

Yes, for the original example posted, the property will work exactly the same as simply having an instance variable ‘x’.

This is the best thing about python properties. From the outside, they work exactly like instance variables! Which allows you to use instance variables from outside the class.

This means your first example could actually use an instance variable. If things changed, and then you decide to change your implementation and a property is useful, the interface to the property would still be the same from code outside the class. A change from instance variable to property has no impact on code outside the class.

Many other languages and programming courses will instruct that a programmer should never expose instance variables, and instead use ‘getters’ and ‘setters’ for any value to be accessed from outside the class, even the simple case as quoted in the question.

Code outside the class with many languages (e.g. Java) use

object.get_i()
    #and
object.set_i(value)

#in place of (with python)
object.i
    #and 
object.i = value

And when implementing the class there are many ‘getters’ and ‘setters’ that do exactly as your first example: replicate a simply instance variable. These getters and setters are required because if the class implementation changes, all the code outside the class will need to change. But python properties allow code outside the class to be the same as with instance variables. So code outside the class does not need to be changed if you add a property, or have a simple instance variable. So unlike most Object Oriented languages, for your simple example you can use the instance variable instead of ‘getters’ and ‘setters’ that are really not needed, secure in the knowledge that if you change to a property in the future, the code using your class need not change.

This means you only need create properties if there is complex behaviour, and for the very common simple case where, as described in the question, a simple instance variable is all that is needed, you can just use the instance variable.


回答 7

与使用setter和getters相比,属性的另一个不错的功能是,它们允许您继续在属性上使用OP =运算符(例如,+ =,-=,* =等),同时仍保留任何验证,访问控制,缓存等设置者和获取者将提供。

例如,如果您Person使用setter setage(newage)和getter 编写了类,getage()则要增加年龄,您必须编写:

bob = Person('Robert', 25)
bob.setage(bob.getage() + 1)

但是如果您age有财产,您可以写得更加简洁:

bob.age += 1

another nice feature of properties over using setters and getters it that they allow you to continue to use OP= operators (eg +=, -=, *= etc) on your attributes while still retaining any validation, access control, caching, etc that the setters and getters would supply.

for example if you wrote the class Person with a setter setage(newage), and a getter getage(), then to increment the age you would have to write:

bob = Person('Robert', 25)
bob.setage(bob.getage() + 1)

but if you made age a property you could write the much cleaner:

bob.age += 1

回答 8

这个问题的简短答案是,在您的示例中,没有任何好处。您可能应该使用不包含属性的表格。

属性存在的原因是,如果您的代码在将来发生更改,并且您突然需要对数据做更多的事情:缓存值,保护访问,查询一些外部资源…等等,您可以轻松地修改类以添加getter和数据的设置器,而无需更改接口,因此您不必在代码中的任何地方找到要访问该数据的地方,也不必进行更改。

The short answer to your question, is that in your example, there is no benefit. You should probably use the form that doesn’t involve properties.

The reason properties exists, is that if your code changes in the future, and you suddenly need to do more with your data: cache values, protect access, query some external resource… whatever, you can easily modify your class to add getters and setters for the data without changing the interface, so you don’t have to find everywhere in your code where that data is accessed and change that too.


回答 9

起初许多人没有注意到的事情是您可以创建自己的属性子类。我发现这对于公开只读对象属性或可以读写但不能删除的属性非常有用。这也是包装诸如跟踪对对象字段的修改之类的功能的绝佳方法。

class reader(property):
    def __init__(self, varname):
        _reader = lambda obj: getattr(obj, varname)
        super(reader, self).__init__(_reader)

class accessor(property):
    def __init__(self, varname, set_validation=None):
        _reader = lambda obj: getattr(obj, varname)
        def _writer(obj, value):
            if set_validation is not None:
               if set_validation(value):
                  setattr(obj, varname, value)
        super(accessor, self).__init__(_reader, _writer)

#example
class MyClass(object):
   def __init__(self):
     self._attr = None

   attr = reader('_attr')

Something that many do not notice at first is that you can make your own subclasses of property. This I have found very useful for exposing read only object attributes or attribute you can read and write but not remove. It is also an excellent way to wrap functionality like tracking modifications to object fields.

class reader(property):
    def __init__(self, varname):
        _reader = lambda obj: getattr(obj, varname)
        super(reader, self).__init__(_reader)

class accessor(property):
    def __init__(self, varname, set_validation=None):
        _reader = lambda obj: getattr(obj, varname)
        def _writer(obj, value):
            if set_validation is not None:
               if set_validation(value):
                  setattr(obj, varname, value)
        super(accessor, self).__init__(_reader, _writer)

#example
class MyClass(object):
   def __init__(self):
     self._attr = None

   attr = reader('_attr')

Python函数重载

问题:Python函数重载

我知道Python不支持方法重载,但是我遇到了一个似乎无法用一种很好的Pythonic方法解决的问题。

我正在制作一个角色需要射击各种子弹的游戏,但是如何编写用于创建这些子弹的不同功能?例如,假设我有一个函数,该函数创建一个以给定速度从A点移动到B点的子弹。我会写一个像这样的函数:

    def add_bullet(sprite, start, headto, speed):
        ... Code ...

但是我想编写其他创建项目符号的功能,例如:

    def add_bullet(sprite, start, direction, speed):
    def add_bullet(sprite, start, headto, spead, acceleration):
    def add_bullet(sprite, script): # For bullets that are controlled by a script
    def add_bullet(sprite, curve, speed): # for bullets with curved paths
    ... And so on ...

以此类推。有没有一种更好的方法可以在不使用太多关键字参数的情况下实现快速更新呢?重命名各功能是非常糟糕的一点,因为你要么add_bullet1add_bullet2add_bullet_with_really_long_name

要解决一些答案:

  1. 不,我无法创建Bullet类层次结构,因为那太慢了。管理项目符号的实际代码在C中,而我的函数是C API的包装器。

  2. 我知道关键字参数,但是检查各种参数组合变得很烦人,但是默认参数可以像 acceleration=0

I know that Python does not support method overloading, but I’ve run into a problem that I can’t seem to solve in a nice Pythonic way.

I am making a game where a character needs to shoot a variety of bullets, but how do I write different functions for creating these bullets? For example suppose I have a function that creates a bullet travelling from point A to B with a given speed. I would write a function like this:

    def add_bullet(sprite, start, headto, speed):
        ... Code ...

But I want to write other functions for creating bullets like:

    def add_bullet(sprite, start, direction, speed):
    def add_bullet(sprite, start, headto, spead, acceleration):
    def add_bullet(sprite, script): # For bullets that are controlled by a script
    def add_bullet(sprite, curve, speed): # for bullets with curved paths
    ... And so on ...

And so on with many variations. Is there a better way to do it without using so many keyword arguments cause its getting kinda ugly fast. Renaming each function is pretty bad too because you get either add_bullet1, add_bullet2, or add_bullet_with_really_long_name.

To address some answers:

  1. No I can’t create a Bullet class hierarchy because thats too slow. The actual code for managing bullets is in C and my functions are wrappers around C API.

  2. I know about the keyword arguments but checking for all sorts of combinations of parameters is getting annoying, but default arguments help allot like acceleration=0


回答 0

您要的就是所谓的多重调度。请参阅Julia语言示例,该示例演示了不同类型的调度。

但是,在查看该内容之前,我们将首先解决为什么重载并不是python中真正想要的。

为什么不超载?

首先,需要了解重载的概念以及为什么它不适用于python。

当使用可在编译时区分数据类型的语言时,可以在编译时进行选择。创建此类用于编译时选择的替代函数的操作通常称为重载函数。(维基百科

Python是一种动态类型的语言,因此重载的概念根本不适用于它。但是,一切都不会丢失,因为我们可以在运行时创建这样的替代函数

在将数据类型标识推迟到运行时的编程语言中,必须根据功能参数的动态确定类型,在运行时进行其他功能的选择。以这种方式选择其替代实现的功能通常被称为多方法。(维基百科

因此,我们应该能够做到多方法在python-或者,它也可称为:多分派

多次派遣

多重方法也称为多重调度

多种调度或多种方法是某些面向对象的编程语言的功能,其中可以基于多个参数的运行时(动态)类型来动态调度函数或方法。(维基百科

Python不支持这个开箱1,但是,因为它发生,有一个优秀的Python包称为multipledispatch这正是这么做的。

这是我们可能如何使用multidispatch 2包来实现您的方法的方法:

>>> from multipledispatch import dispatch
>>> from collections import namedtuple  
>>> from types import *  # we can test for lambda type, e.g.:
>>> type(lambda a: 1) == LambdaType
True

>>> Sprite = namedtuple('Sprite', ['name'])
>>> Point = namedtuple('Point', ['x', 'y'])
>>> Curve = namedtuple('Curve', ['x', 'y', 'z'])
>>> Vector = namedtuple('Vector', ['x','y','z'])

>>> @dispatch(Sprite, Point, Vector, int)
... def add_bullet(sprite, start, direction, speed):
...     print("Called Version 1")
...
>>> @dispatch(Sprite, Point, Point, int, float)
... def add_bullet(sprite, start, headto, speed, acceleration):
...     print("Called version 2")
...
>>> @dispatch(Sprite, LambdaType)
... def add_bullet(sprite, script):
...     print("Called version 3")
...
>>> @dispatch(Sprite, Curve, int)
... def add_bullet(sprite, curve, speed):
...     print("Called version 4")
...

>>> sprite = Sprite('Turtle')
>>> start = Point(1,2)
>>> direction = Vector(1,1,1)
>>> speed = 100 #km/h
>>> acceleration = 5.0 #m/s
>>> script = lambda sprite: sprite.x * 2
>>> curve = Curve(3, 1, 4)
>>> headto = Point(100, 100) # somewhere far away

>>> add_bullet(sprite, start, direction, speed)
Called Version 1

>>> add_bullet(sprite, start, headto, speed, acceleration)
Called version 2

>>> add_bullet(sprite, script)
Called version 3

>>> add_bullet(sprite, curve, speed)
Called version 4

1. Python 3当前支持单调度 。2.注意不要在多线程环境中使用 调度,否则会出现奇怪的行为。

What you are asking for is called multiple dispatch. See Julia language examples which demonstrates different types of dispatches.

However, before looking at that, we’ll first tackle why overloading is not really what you want in python.

Why Not Overloading?

First, one needs to understand the concept of overloading and why it’s not applicable to python.

When working with languages that can discriminate data types at compile-time, selecting among the alternatives can occur at compile-time. The act of creating such alternative functions for compile-time selection is usually referred to as overloading a function. (Wikipedia)

Python is a dynamically typed language, so the concept of overloading simply does not apply to it. However, all is not lost, since we can create such alternative functions at run-time:

In programming languages that defer data type identification until run-time the selection among alternative functions must occur at run-time, based on the dynamically determined types of function arguments. Functions whose alternative implementations are selected in this manner are referred to most generally as multimethods. (Wikipedia)

So we should be able to do multimethods in python—or, as it is alternatively called: multiple dispatch.

Multiple dispatch

The multimethods are also called multiple dispatch:

Multiple dispatch or multimethods is the feature of some object-oriented programming languages in which a function or method can be dynamically dispatched based on the run time (dynamic) type of more than one of its arguments. (Wikipedia)

Python does not support this out of the box1, but, as it happens, there is an excellent python package called multipledispatch that does exactly that.

Solution

Here is how we might use multipledispatch2 package to implement your methods:

>>> from multipledispatch import dispatch
>>> from collections import namedtuple  
>>> from types import *  # we can test for lambda type, e.g.:
>>> type(lambda a: 1) == LambdaType
True

>>> Sprite = namedtuple('Sprite', ['name'])
>>> Point = namedtuple('Point', ['x', 'y'])
>>> Curve = namedtuple('Curve', ['x', 'y', 'z'])
>>> Vector = namedtuple('Vector', ['x','y','z'])

>>> @dispatch(Sprite, Point, Vector, int)
... def add_bullet(sprite, start, direction, speed):
...     print("Called Version 1")
...
>>> @dispatch(Sprite, Point, Point, int, float)
... def add_bullet(sprite, start, headto, speed, acceleration):
...     print("Called version 2")
...
>>> @dispatch(Sprite, LambdaType)
... def add_bullet(sprite, script):
...     print("Called version 3")
...
>>> @dispatch(Sprite, Curve, int)
... def add_bullet(sprite, curve, speed):
...     print("Called version 4")
...

>>> sprite = Sprite('Turtle')
>>> start = Point(1,2)
>>> direction = Vector(1,1,1)
>>> speed = 100 #km/h
>>> acceleration = 5.0 #m/s
>>> script = lambda sprite: sprite.x * 2
>>> curve = Curve(3, 1, 4)
>>> headto = Point(100, 100) # somewhere far away

>>> add_bullet(sprite, start, direction, speed)
Called Version 1

>>> add_bullet(sprite, start, headto, speed, acceleration)
Called version 2

>>> add_bullet(sprite, script)
Called version 3

>>> add_bullet(sprite, curve, speed)
Called version 4

1. Python 3 currently supports single dispatch
2. Take care not to use multipledispatch in a multi-threaded environment or you will get weird behavior.


回答 1

演示时,Python确实支持“方法重载”。实际上,您刚才描述的内容在Python中以许多不同的方式实现都是微不足道的,但我会同意:

class Character(object):
    # your character __init__ and other methods go here

    def add_bullet(self, sprite=default, start=default, 
                 direction=default, speed=default, accel=default, 
                  curve=default):
        # do stuff with your arguments

在上面的代码中,default是这些参数的合理默认值或None。然后,您可以仅使用您感兴趣的参数来调用该方法,Python将使用默认值。

您还可以执行以下操作:

class Character(object):
    # your character __init__ and other methods go here

    def add_bullet(self, **kwargs):
        # here you can unpack kwargs as (key, values) and
        # do stuff with them, and use some global dictionary
        # to provide default values and ensure that ``key``
        # is a valid argument...

        # do stuff with your arguments

另一种选择是直接将所需函数直接挂接到类或实例上:

def some_implementation(self, arg1, arg2, arg3):
  # implementation
my_class.add_bullet = some_implementation_of_add_bullet

另一种方法是使用抽象工厂模式:

class Character(object):
   def __init__(self, bfactory, *args, **kwargs):
       self.bfactory = bfactory
   def add_bullet(self):
       sprite = self.bfactory.sprite()
       speed = self.bfactory.speed()
       # do stuff with your sprite and speed

class pretty_and_fast_factory(object):
    def sprite(self):
       return pretty_sprite
    def speed(self):
       return 10000000000.0

my_character = Character(pretty_and_fast_factory(), a1, a2, kw1=v1, kw2=v2)
my_character.add_bullet() # uses pretty_and_fast_factory

# now, if you have another factory called "ugly_and_slow_factory" 
# you can change it at runtime in python by issuing
my_character.bfactory = ugly_and_slow_factory()

# In the last example you can see abstract factory and "method
# overloading" (as you call it) in action 

Python does support “method overloading” as you present it. In fact, what you just describe is trivial to implement in Python, in so many different ways, but I would go with:

class Character(object):
    # your character __init__ and other methods go here

    def add_bullet(self, sprite=default, start=default, 
                 direction=default, speed=default, accel=default, 
                  curve=default):
        # do stuff with your arguments

In the above code, default is a plausible default value for those arguments, or None. You can then call the method with only the arguments you are interested in, and Python will use the default values.

You could also do something like this:

class Character(object):
    # your character __init__ and other methods go here

    def add_bullet(self, **kwargs):
        # here you can unpack kwargs as (key, values) and
        # do stuff with them, and use some global dictionary
        # to provide default values and ensure that ``key``
        # is a valid argument...

        # do stuff with your arguments

Another alternative is to directly hook the desired function directly to the class or instance:

def some_implementation(self, arg1, arg2, arg3):
  # implementation
my_class.add_bullet = some_implementation_of_add_bullet

Yet another way is to use an abstract factory pattern:

class Character(object):
   def __init__(self, bfactory, *args, **kwargs):
       self.bfactory = bfactory
   def add_bullet(self):
       sprite = self.bfactory.sprite()
       speed = self.bfactory.speed()
       # do stuff with your sprite and speed

class pretty_and_fast_factory(object):
    def sprite(self):
       return pretty_sprite
    def speed(self):
       return 10000000000.0

my_character = Character(pretty_and_fast_factory(), a1, a2, kw1=v1, kw2=v2)
my_character.add_bullet() # uses pretty_and_fast_factory

# now, if you have another factory called "ugly_and_slow_factory" 
# you can change it at runtime in python by issuing
my_character.bfactory = ugly_and_slow_factory()

# In the last example you can see abstract factory and "method
# overloading" (as you call it) in action 

回答 2

您可以使用“自己动手”解决方案进行函数重载。这是从Guido van Rossum关于多方法的文章中复制(因为mm和python中的重载之间几乎没有区别):

registry = {}

class MultiMethod(object):
    def __init__(self, name):
        self.name = name
        self.typemap = {}
    def __call__(self, *args):
        types = tuple(arg.__class__ for arg in args) # a generator expression!
        function = self.typemap.get(types)
        if function is None:
            raise TypeError("no match")
        return function(*args)
    def register(self, types, function):
        if types in self.typemap:
            raise TypeError("duplicate registration")
        self.typemap[types] = function


def multimethod(*types):
    def register(function):
        name = function.__name__
        mm = registry.get(name)
        if mm is None:
            mm = registry[name] = MultiMethod(name)
        mm.register(types, function)
        return mm
    return register

用法是

from multimethods import multimethod
import unittest

# 'overload' makes more sense in this case
overload = multimethod

class Sprite(object):
    pass

class Point(object):
    pass

class Curve(object):
    pass

@overload(Sprite, Point, Direction, int)
def add_bullet(sprite, start, direction, speed):
    # ...

@overload(Sprite, Point, Point, int, int)
def add_bullet(sprite, start, headto, speed, acceleration):
    # ...

@overload(Sprite, str)
def add_bullet(sprite, script):
    # ...

@overload(Sprite, Curve, speed)
def add_bullet(sprite, curve, speed):
    # ...

最严格的限制,目前主要有:

  • 不支持方法,仅支持不是类成员的函数;
  • 不处理继承;
  • 不支持kwarg;
  • 注册新功能应该在导入时完成,这不是线程安全的

You can use “roll-your-own” solution for function overloading. This one is copied from Guido van Rossum’s article about multimethods (because there is little difference between mm and overloading in python):

registry = {}

class MultiMethod(object):
    def __init__(self, name):
        self.name = name
        self.typemap = {}
    def __call__(self, *args):
        types = tuple(arg.__class__ for arg in args) # a generator expression!
        function = self.typemap.get(types)
        if function is None:
            raise TypeError("no match")
        return function(*args)
    def register(self, types, function):
        if types in self.typemap:
            raise TypeError("duplicate registration")
        self.typemap[types] = function


def multimethod(*types):
    def register(function):
        name = function.__name__
        mm = registry.get(name)
        if mm is None:
            mm = registry[name] = MultiMethod(name)
        mm.register(types, function)
        return mm
    return register

The usage would be

from multimethods import multimethod
import unittest

# 'overload' makes more sense in this case
overload = multimethod

class Sprite(object):
    pass

class Point(object):
    pass

class Curve(object):
    pass

@overload(Sprite, Point, Direction, int)
def add_bullet(sprite, start, direction, speed):
    # ...

@overload(Sprite, Point, Point, int, int)
def add_bullet(sprite, start, headto, speed, acceleration):
    # ...

@overload(Sprite, str)
def add_bullet(sprite, script):
    # ...

@overload(Sprite, Curve, speed)
def add_bullet(sprite, curve, speed):
    # ...

Most restrictive limitations at the moment are:

  • methods are not supported, only functions that are not class members;
  • inheritance is not handled;
  • kwargs are not supported;
  • registering new functions should be done at import time thing is not thread-safe

回答 3

一个可能的选择是使用Multipledispatch模块,如下所示: http //matthewrocklin.com/blog/work/2014/02/25/Multiple-Dispatch

而不是这样做:

def add(self, other):
    if isinstance(other, Foo):
        ...
    elif isinstance(other, Bar):
        ...
    else:
        raise NotImplementedError()

你可以这样做:

from multipledispatch import dispatch
@dispatch(int, int)
def add(x, y):
    return x + y    

@dispatch(object, object)
def add(x, y):
    return "%s + %s" % (x, y)

使用结果:

>>> add(1, 2)
3

>>> add(1, 'hello')
'1 + hello'

A possible option is to use the multipledispatch module as detailed here: http://matthewrocklin.com/blog/work/2014/02/25/Multiple-Dispatch

Instead of doing this:

def add(self, other):
    if isinstance(other, Foo):
        ...
    elif isinstance(other, Bar):
        ...
    else:
        raise NotImplementedError()

You can do this:

from multipledispatch import dispatch
@dispatch(int, int)
def add(x, y):
    return x + y    

@dispatch(object, object)
def add(x, y):
    return "%s + %s" % (x, y)

With the resulting usage:

>>> add(1, 2)
3

>>> add(1, 'hello')
'1 + hello'

回答 4

在Python 3.4中添加了PEP-0443。单调度通用函数

这是来自PEP的简短API描述。

要定义通用函数,请使用@singledispatch装饰器对其进行装饰。请注意,调度是根据第一个参数的类型进行的。相应地创建函数:

from functools import singledispatch
@singledispatch
def fun(arg, verbose=False):
    if verbose:
        print("Let me just say,", end=" ")
    print(arg)

要将重载的实现添加到函数中,请使用泛型函数的register()属性。这是一个装饰器,接受一个类型参数,并装饰实现该类型的操作的函数:

@fun.register(int)
def _(arg, verbose=False):
    if verbose:
        print("Strength in numbers, eh?", end=" ")
    print(arg)

@fun.register(list)
def _(arg, verbose=False):
    if verbose:
        print("Enumerate this:")
    for i, elem in enumerate(arg):
        print(i, elem)

In Python 3.4 was added PEP-0443. Single-dispatch generic functions.

Here is short API description from PEP.

To define a generic function, decorate it with the @singledispatch decorator. Note that the dispatch happens on the type of the first argument. Create your function accordingly:

from functools import singledispatch
@singledispatch
def fun(arg, verbose=False):
    if verbose:
        print("Let me just say,", end=" ")
    print(arg)

To add overloaded implementations to the function, use the register() attribute of the generic function. This is a decorator, taking a type parameter and decorating a function implementing the operation for that type:

@fun.register(int)
def _(arg, verbose=False):
    if verbose:
        print("Strength in numbers, eh?", end=" ")
    print(arg)

@fun.register(list)
def _(arg, verbose=False):
    if verbose:
        print("Enumerate this:")
    for i, elem in enumerate(arg):
        print(i, elem)

回答 5

通常使用多态来解决这种类型的行为(在OOP语言中)。每种类型的子弹都将负责知道它的运动方式。例如:

class Bullet(object):
    def __init__(self):
        self.curve = None
        self.speed = None
        self.acceleration = None
        self.sprite_image = None

class RegularBullet(Bullet):
    def __init__(self):
        super(RegularBullet, self).__init__()
        self.speed = 10

class Grenade(Bullet):
    def __init__(self):
        super(Grenade, self).__init__()
        self.speed = 4
        self.curve = 3.5

add_bullet(Grendade())

def add_bullet(bullet):
    c_function(bullet.speed, bullet.curve, bullet.acceleration, bullet.sprite, bullet.x, bullet.y) 


void c_function(double speed, double curve, double accel, char[] sprite, ...) {
    if (speed != null && ...) regular_bullet(...)
    else if (...) curved_bullet(...)
    //..etc..
}

将尽可能多的参数传递给存在的c_function,然后执行基于初始c函数中的值确定要调用哪个c函数的工作。因此,python应该只调用一个c函数。一个c函数查看参数,然后可以适当地委派给其他c函数。

从本质上讲,您只是将每个子类用作不同的数据容器,但是通过在基类上定义所有可能的参数,这些子类可以随意忽略它们不执行的操作。

当出现一种新型的项目符号时,您可以简单地在基础上定义另一个属性,更改一个python函数以使其传递额外的属性,然后更改一个c_function来检查参数并适当地委派。我猜听起来还不错。

This type of behaviour is typically solved (in OOP languages) using Polymorphism. Each type of bullet would be responsible for knowing how it travels. For instance:

class Bullet(object):
    def __init__(self):
        self.curve = None
        self.speed = None
        self.acceleration = None
        self.sprite_image = None

class RegularBullet(Bullet):
    def __init__(self):
        super(RegularBullet, self).__init__()
        self.speed = 10

class Grenade(Bullet):
    def __init__(self):
        super(Grenade, self).__init__()
        self.speed = 4
        self.curve = 3.5

add_bullet(Grendade())

def add_bullet(bullet):
    c_function(bullet.speed, bullet.curve, bullet.acceleration, bullet.sprite, bullet.x, bullet.y) 


void c_function(double speed, double curve, double accel, char[] sprite, ...) {
    if (speed != null && ...) regular_bullet(...)
    else if (...) curved_bullet(...)
    //..etc..
}

Pass as many arguments to the c_function that exist, then do the job of determining which c function to call based on the values in the initial c function. So, python should only ever be calling the one c function. That one c function looks at the arguments, and then can delegate to other c functions appropriately.

You’re essentially just using each subclass as a different data container, but by defining all the potential arguments on the base class, the subclasses are free to ignore the ones they do nothing with.

When a new type of bullet comes along, you can simply define one more property on the base, change the one python function so that it passes the extra property, and the one c_function that examines the arguments and delegates appropriately. Doesn’t sound too bad I guess.


回答 6

通过传递关键字args

def add_bullet(**kwargs):
    #check for the arguments listed above and do the proper things

By passing keyword args.

def add_bullet(**kwargs):
    #check for the arguments listed above and do the proper things

回答 7

在定义中使用多个关键字参数,或创建一个将Bullet其实例传递给该函数的层次结构。

Either use multiple keyword arguments in the definition, or create a Bullet hierarchy whose instances are passed to the function.


回答 8

我认为您的基本要求是在python中使用C / C ++之类的语法,并尽可能减少麻烦。尽管我喜欢Alexander Poluektov的回答,但不适用于课堂。

以下内容适用于类。它通过按非关键字参数的数量区分来工作(但不支持按类型区分):

class TestOverloading(object):
    def overloaded_function(self, *args, **kwargs):
        # Call the function that has the same number of non-keyword arguments.  
        getattr(self, "_overloaded_function_impl_" + str(len(args)))(*args, **kwargs)
    
    def _overloaded_function_impl_3(self, sprite, start, direction, **kwargs):
        print "This is overload 3"
        print "Sprite: %s" % str(sprite)
        print "Start: %s" % str(start)
        print "Direction: %s" % str(direction)
        
    def _overloaded_function_impl_2(self, sprite, script):
        print "This is overload 2"
        print "Sprite: %s" % str(sprite)
        print "Script: "
        print script

它可以像这样简单地使用:

test = TestOverloading()

test.overloaded_function("I'm a Sprite", 0, "Right")
print
test.overloaded_function("I'm another Sprite", "while x == True: print 'hi'")

输出:

这是过载3
雪碧:我是雪碧
开始:0
方向:正确

这是重载2
Sprite:我是另一个Sprite
脚本:
而x == True:print’hi’

I think your basic requirement is to have a C/C++ like syntax in python with the least headache possible. Although I liked Alexander Poluektov’s answer it doesn’t work for classes.

The following should work for classes. It works by distinguishing by the number of non keyword arguments (but doesn’t support distinguishing by type):

class TestOverloading(object):
    def overloaded_function(self, *args, **kwargs):
        # Call the function that has the same number of non-keyword arguments.  
        getattr(self, "_overloaded_function_impl_" + str(len(args)))(*args, **kwargs)
    
    def _overloaded_function_impl_3(self, sprite, start, direction, **kwargs):
        print "This is overload 3"
        print "Sprite: %s" % str(sprite)
        print "Start: %s" % str(start)
        print "Direction: %s" % str(direction)
        
    def _overloaded_function_impl_2(self, sprite, script):
        print "This is overload 2"
        print "Sprite: %s" % str(sprite)
        print "Script: "
        print script

And it can be used simply like this:

test = TestOverloading()

test.overloaded_function("I'm a Sprite", 0, "Right")
print
test.overloaded_function("I'm another Sprite", "while x == True: print 'hi'")

Output:

This is overload 3
Sprite: I’m a Sprite
Start: 0
Direction: Right

This is overload 2
Sprite: I’m another Sprite
Script:
while x == True: print ‘hi’


回答 9

@overload用类型的提示(PEP 484)添加装饰器。尽管这不会改变python的行为,但确实可以更轻松地了解正在发生的事情,并让mypy检测错误。
请参阅:键入提示PEP 484

The @overload decorator was added with type hints (PEP 484). While this doesn’t change the behaviour of python, it does make it easier to understand what is going on, and for mypy to detect errors.
See: Type hints and PEP 484


回答 10

我认为Bullet具有相关多态性的类层次结构是必经之路。您可以通过使用元类有效地重载基类构造函数,以便调用基类可导致创建适当的子类对象。下面是一些示例代码,以说明我的意思。

更新

该代码已经过修改,可以在Python 2和3下运行,以保持相关性。这样做的方式避免了使用Python的显式元类语法,该语法在两个版本之间有所不同。

为了实现这一目标,一个BulletMetaBase的实例BulletMeta类是由创建时显式调用元类来创建Bullet基类(而不是使用__metaclass__=类属性或通过metaclass取决于Python版本关键字参数)。

class BulletMeta(type):
    def __new__(cls, classname, bases, classdict):
        """ Create Bullet class or a subclass of it. """
        classobj = type.__new__(cls, classname, bases, classdict)
        if classname != 'BulletMetaBase':
            if classname == 'Bullet':  # Base class definition?
                classobj.registry = {}  # Initialize subclass registry.
            else:
                try:
                    alias = classdict['alias']
                except KeyError:
                    raise TypeError("Bullet subclass %s has no 'alias'" %
                                    classname)
                if alias in Bullet.registry: # unique?
                    raise TypeError("Bullet subclass %s's alias attribute "
                                    "%r already in use" % (classname, alias))
                # Register subclass under the specified alias.
                classobj.registry[alias] = classobj

        return classobj

    def __call__(cls, alias, *args, **kwargs):
        """ Bullet subclasses instance factory.

            Subclasses should only be instantiated by calls to the base
            class with their subclass' alias as the first arg.
        """
        if cls != Bullet:
            raise TypeError("Bullet subclass %r objects should not to "
                            "be explicitly constructed." % cls.__name__)
        elif alias not in cls.registry: # Bullet subclass?
            raise NotImplementedError("Unknown Bullet subclass %r" %
                                      str(alias))
        # Create designated subclass object (call its __init__ method).
        subclass = cls.registry[alias]
        return type.__call__(subclass, *args, **kwargs)


class Bullet(BulletMeta('BulletMetaBase', (object,), {})):
    # Presumably you'd define some abstract methods that all here
    # that would be supported by all subclasses.
    # These definitions could just raise NotImplementedError() or
    # implement the functionality is some sub-optimal generic way.
    # For example:
    def fire(self, *args, **kwargs):
        raise NotImplementedError(self.__class__.__name__ + ".fire() method")

    # Abstract base class's __init__ should never be called.
    # If subclasses need to call super class's __init__() for some
    # reason then it would need to be implemented.
    def __init__(self, *args, **kwargs):
        raise NotImplementedError("Bullet is an abstract base class")


# Subclass definitions.
class Bullet1(Bullet):
    alias = 'B1'
    def __init__(self, sprite, start, direction, speed):
        print('creating %s object' % self.__class__.__name__)
    def fire(self, trajectory):
        print('Bullet1 object fired with %s trajectory' % trajectory)


class Bullet2(Bullet):
    alias = 'B2'
    def __init__(self, sprite, start, headto, spead, acceleration):
        print('creating %s object' % self.__class__.__name__)


class Bullet3(Bullet):
    alias = 'B3'
    def __init__(self, sprite, script): # script controlled bullets
        print('creating %s object' % self.__class__.__name__)


class Bullet4(Bullet):
    alias = 'B4'
    def __init__(self, sprite, curve, speed): # for bullets with curved paths
        print('creating %s object' % self.__class__.__name__)


class Sprite: pass
class Curve: pass

b1 = Bullet('B1', Sprite(), (10,20,30), 90, 600)
b2 = Bullet('B2', Sprite(), (-30,17,94), (1,-1,-1), 600, 10)
b3 = Bullet('B3', Sprite(), 'bullet42.script')
b4 = Bullet('B4', Sprite(), Curve(), 720)
b1.fire('uniform gravity')
b2.fire('uniform gravity')

输出:

creating Bullet1 object
creating Bullet2 object
creating Bullet3 object
creating Bullet4 object
Bullet1 object fired with uniform gravity trajectory
Traceback (most recent call last):
  File "python-function-overloading.py", line 93, in <module>
    b2.fire('uniform gravity') # NotImplementedError: Bullet2.fire() method
  File "python-function-overloading.py", line 49, in fire
    raise NotImplementedError(self.__class__.__name__ + ".fire() method")
NotImplementedError: Bullet2.fire() method

I think a Bullet class hierarchy with the associated polymorphism is the way to go. You can effectively overload the base class constructor by using a metaclass so that calling the base class results in the creation of the appropriate subclass object. Below is some sample code to illustrate the essence of what I mean.

Updated

The code has been modified to run under both Python 2 and 3 to keep it relevant. This was done in a way that avoids the use Python’s explicit metaclass syntax, which varies between the two versions.

To accomplish that objective, a BulletMetaBase instance of the BulletMeta class is created by explicitly calling the metaclass when creating the Bullet baseclass (rather than using the __metaclass__= class attribute or via a metaclass keyword argument depending on the Python version).

class BulletMeta(type):
    def __new__(cls, classname, bases, classdict):
        """ Create Bullet class or a subclass of it. """
        classobj = type.__new__(cls, classname, bases, classdict)
        if classname != 'BulletMetaBase':
            if classname == 'Bullet':  # Base class definition?
                classobj.registry = {}  # Initialize subclass registry.
            else:
                try:
                    alias = classdict['alias']
                except KeyError:
                    raise TypeError("Bullet subclass %s has no 'alias'" %
                                    classname)
                if alias in Bullet.registry: # unique?
                    raise TypeError("Bullet subclass %s's alias attribute "
                                    "%r already in use" % (classname, alias))
                # Register subclass under the specified alias.
                classobj.registry[alias] = classobj

        return classobj

    def __call__(cls, alias, *args, **kwargs):
        """ Bullet subclasses instance factory.

            Subclasses should only be instantiated by calls to the base
            class with their subclass' alias as the first arg.
        """
        if cls != Bullet:
            raise TypeError("Bullet subclass %r objects should not to "
                            "be explicitly constructed." % cls.__name__)
        elif alias not in cls.registry: # Bullet subclass?
            raise NotImplementedError("Unknown Bullet subclass %r" %
                                      str(alias))
        # Create designated subclass object (call its __init__ method).
        subclass = cls.registry[alias]
        return type.__call__(subclass, *args, **kwargs)


class Bullet(BulletMeta('BulletMetaBase', (object,), {})):
    # Presumably you'd define some abstract methods that all here
    # that would be supported by all subclasses.
    # These definitions could just raise NotImplementedError() or
    # implement the functionality is some sub-optimal generic way.
    # For example:
    def fire(self, *args, **kwargs):
        raise NotImplementedError(self.__class__.__name__ + ".fire() method")

    # Abstract base class's __init__ should never be called.
    # If subclasses need to call super class's __init__() for some
    # reason then it would need to be implemented.
    def __init__(self, *args, **kwargs):
        raise NotImplementedError("Bullet is an abstract base class")


# Subclass definitions.
class Bullet1(Bullet):
    alias = 'B1'
    def __init__(self, sprite, start, direction, speed):
        print('creating %s object' % self.__class__.__name__)
    def fire(self, trajectory):
        print('Bullet1 object fired with %s trajectory' % trajectory)


class Bullet2(Bullet):
    alias = 'B2'
    def __init__(self, sprite, start, headto, spead, acceleration):
        print('creating %s object' % self.__class__.__name__)


class Bullet3(Bullet):
    alias = 'B3'
    def __init__(self, sprite, script): # script controlled bullets
        print('creating %s object' % self.__class__.__name__)


class Bullet4(Bullet):
    alias = 'B4'
    def __init__(self, sprite, curve, speed): # for bullets with curved paths
        print('creating %s object' % self.__class__.__name__)


class Sprite: pass
class Curve: pass

b1 = Bullet('B1', Sprite(), (10,20,30), 90, 600)
b2 = Bullet('B2', Sprite(), (-30,17,94), (1,-1,-1), 600, 10)
b3 = Bullet('B3', Sprite(), 'bullet42.script')
b4 = Bullet('B4', Sprite(), Curve(), 720)
b1.fire('uniform gravity')
b2.fire('uniform gravity')

Output:

creating Bullet1 object
creating Bullet2 object
creating Bullet3 object
creating Bullet4 object
Bullet1 object fired with uniform gravity trajectory
Traceback (most recent call last):
  File "python-function-overloading.py", line 93, in <module>
    b2.fire('uniform gravity') # NotImplementedError: Bullet2.fire() method
  File "python-function-overloading.py", line 49, in fire
    raise NotImplementedError(self.__class__.__name__ + ".fire() method")
NotImplementedError: Bullet2.fire() method

回答 11

Python 3.8添加了functools.singledispatchmethod

将方法转换为单调度通用函数。

要定义通用方法,请使用@singledispatchmethod装饰器对其进行装饰。请注意,调度是根据第一个非自身或非cls参数的类型进行的,请相应地创建函数:

from functools import singledispatchmethod


class Negator:
    @singledispatchmethod
    def neg(self, arg):
        raise NotImplementedError("Cannot negate a")

    @neg.register
    def _(self, arg: int):
        return -arg

    @neg.register
    def _(self, arg: bool):
        return not arg


negator = Negator()
for v in [42, True, "Overloading"]:
    neg = negator.neg(v)
    print(f"{v=}, {neg=}")

输出量

v=42, neg=-42
v=True, neg=False
NotImplementedError: Cannot negate a

@singledispatchmethod支持与其他装饰器(例如,@ classmethod)嵌套。请注意,要允许dispatcher.register,singledispatchmethod必须是最外面的装饰器。这是Negator类,其中neg方法绑定了类:

from functools import singledispatchmethod


class Negator:
    @singledispatchmethod
    @staticmethod
    def neg(arg):
        raise NotImplementedError("Cannot negate a")

    @neg.register
    def _(arg: int) -> int:
        return -arg

    @neg.register
    def _(arg: bool) -> bool:
        return not arg


for v in [42, True, "Overloading"]:
    neg = Negator.neg(v)
    print(f"{v=}, {neg=}")

输出:

v=42, neg=-42
v=True, neg=False
NotImplementedError: Cannot negate a

相同的模式可用于其他类似的修饰符:staticmethod,abstractmethod等。

Python 3.8 added functools.singledispatchmethod

Transform a method into a single-dispatch generic function.

To define a generic method, decorate it with the @singledispatchmethod decorator. Note that the dispatch happens on the type of the first non-self or non-cls argument, create your function accordingly:

from functools import singledispatchmethod


class Negator:
    @singledispatchmethod
    def neg(self, arg):
        raise NotImplementedError("Cannot negate a")

    @neg.register
    def _(self, arg: int):
        return -arg

    @neg.register
    def _(self, arg: bool):
        return not arg


negator = Negator()
for v in [42, True, "Overloading"]:
    neg = negator.neg(v)
    print(f"{v=}, {neg=}")

Output

v=42, neg=-42
v=True, neg=False
NotImplementedError: Cannot negate a

@singledispatchmethod supports nesting with other decorators such as @classmethod. Note that to allow for dispatcher.register, singledispatchmethod must be the outer most decorator. Here is the Negator class with the neg methods being class bound:

from functools import singledispatchmethod


class Negator:
    @singledispatchmethod
    @staticmethod
    def neg(arg):
        raise NotImplementedError("Cannot negate a")

    @neg.register
    def _(arg: int) -> int:
        return -arg

    @neg.register
    def _(arg: bool) -> bool:
        return not arg


for v in [42, True, "Overloading"]:
    neg = Negator.neg(v)
    print(f"{v=}, {neg=}")

Output:

v=42, neg=-42
v=True, neg=False
NotImplementedError: Cannot negate a

The same pattern can be used for other similar decorators: staticmethod, abstractmethod, and others.


回答 12

将关键字参数与默认值一起使用。例如

def add_bullet(sprite, start=default, direction=default, script=default, speed=default):

对于直子弹和弯曲子弹,我将添加两个函数:add_bullet_straightadd_bullet_curved

Use keyword arguments with defaults. E.g.

def add_bullet(sprite, start=default, direction=default, script=default, speed=default):

In the case of a straight bullet versus a curved bullet, I’d add two functions: add_bullet_straight and add_bullet_curved.


回答 13

重载方法在python中很棘手。但是,可能会使用传递字典,列表或原始变量的用法。

我已经为用例尝试过一些方法,这可以帮助您了解人们如何重载方法。

让我们举个例子:

一个类重载方法,其中调用了来自不同类的方法。

def add_bullet(sprite=None, start=None, headto=None, spead=None, acceleration=None):

从远程类传递参数:

add_bullet(sprite = 'test', start=Yes,headto={'lat':10.6666,'long':10.6666},accelaration=10.6}

要么

add_bullet(sprite = 'test', start=Yes, headto={'lat':10.6666,'long':10.6666},speed=['10','20,'30']}

因此,正在通过方法重载实现列表,字典或原始变量的处理。

试试看您的代码。

overloading methods is tricky in python. However, there could be usage of passing the dict, list or primitive variables.

I have tried something for my use cases, this could help here to understand people to overload the methods.

Let’s take your example:

a class overload method with call the methods from different class.

def add_bullet(sprite=None, start=None, headto=None, spead=None, acceleration=None):

pass the arguments from remote class:

add_bullet(sprite = 'test', start=Yes,headto={'lat':10.6666,'long':10.6666},accelaration=10.6}

OR

add_bullet(sprite = 'test', start=Yes, headto={'lat':10.6666,'long':10.6666},speed=['10','20,'30']}

So, handling is being achieved for list, Dictionary or primitive variables from method overloading.

try it out for your codes.


回答 14

只是一个简单的装饰

class overload:
    def __init__(self, f):
        self.cases = {}

    def args(self, *args):
        def store_function(f):
            self.cases[tuple(args)] = f
            return self
        return store_function

    def __call__(self, *args):
        function = self.cases[tuple(type(arg) for arg in args)]
        return function(*args)

你可以这样使用

@overload
def f():
    pass

@f.args(int, int)
def f(x, y):
    print('two integers')

@f.args(float)
def f(x):
    print('one float')


f(5.5)
f(1, 2)

对其进行修改以使其适应您的用例。

概念澄清

  • 功能调度:具有相同名称的多个函数。应该叫哪一个?两种策略
  • 静态/编译时调度也称为“超载”)。根据编译时间确定要调用的函数参数类型的函数。在所有动态语言中,没有编译时类型,因此根据定义,重载是不可能的
  • 动态/运行时分派:根据参数的运行时类型决定要调用的函数。这就是所有OOP语言所要做的:多个类具有相同的方法,并且该语言根据self/this参数的类型决定要调用的是哪种。但是,大多数语言仅将其用于this参数。上面的装饰器将构思扩展到多个参数。

要清除,假定使用静态语言,然后定义功能

void f(Integer x):
    print('integer called')

void f(Float x):
    print('float called')

void f(Number x):
    print('number called')


Number x = new Integer('5')
f(x)
x = new Number('3.14')
f(x)

在静态分派(超载)中,您将看到两次“被调用”,因为x已被声明为Number,这就是所有超载所关心的。使用动态分派,您将看到“整数调用,浮点调用”,因为这些x是调用函数时的实际类型。

Just a simple decorator

class overload:
    def __init__(self, f):
        self.cases = {}

    def args(self, *args):
        def store_function(f):
            self.cases[tuple(args)] = f
            return self
        return store_function

    def __call__(self, *args):
        function = self.cases[tuple(type(arg) for arg in args)]
        return function(*args)

You can use it like this

@overload
def f():
    pass

@f.args(int, int)
def f(x, y):
    print('two integers')

@f.args(float)
def f(x):
    print('one float')


f(5.5)
f(1, 2)

Modify it to adapt it to your use case.

A clarification of concepts

  • function dispatch: there are multiple functions with the same name. Which one should be called? two strategies
  • static/compile-time dispatch (aka. “overloading”). decide which function to call based on the compile-time type of the arguments. In all dynamic languages, there is no compile-time type, so overloading is impossible by definition
  • dynamic/run-time dispatch: decide which function to call based on the runtime type of the arguments. This is what all OOP languages do: multiple classes have the same methods, and the language decides which one to call based on the type of self/this argument. However, most languages only do it for the this argument only. The above decorator extends the idea to multiple parameters.

To clear up, assume a static language, and define the functions

void f(Integer x):
    print('integer called')

void f(Float x):
    print('float called')

void f(Number x):
    print('number called')


Number x = new Integer('5')
f(x)
x = new Number('3.14')
f(x)

With static dispatch (overloading) you will see “number called” twice, because x has been declared as Number, and that’s all overloading cares about. With dynamic dispatch you will see “integer called, float called”, because those are the actual types of x at the time the function is called.


了解__getattr__和__getattribute__之间的区别

问题:了解__getattr__和__getattribute__之间的区别

我试图理解上的差异之间__getattr____getattribute__,但是,我在它失败。

堆栈溢出问题的答案与vs 之间的区别是__getattr____getattribute__

__getattribute__在查看对象的实际属性之前调用,因此很难正确实现。您可以非常轻松地进行无限递归。

我完全不知道那是什么意思。

然后继续说:

您几乎可以肯定想要__getattr__

为什么?

我读到,如果__getattribute__失败,__getattr__则称为。那么,为什么有两种不同的方法做同样的事情呢?如果我的代码实现了新样式类,我应该使用什么?

我正在寻找一些代码示例来清除此问题。我已尽我所能搜索Google,但是我发现的答案并未彻底讨论该问题。

如果有任何文档,我准备阅读。

I am trying to understand the difference between __getattr__ and __getattribute__, however, I am failing at it.

The answer to the Stack Overflow question Difference between __getattr__ vs __getattribute__ says:

__getattribute__ is invoked before looking at the actual attributes on the object, and so can be tricky to implement correctly. You can end up in infinite recursions very easily.

I have absolutely no idea what that means.

Then it goes on to say:

You almost certainly want __getattr__.

Why?

I read that if __getattribute__ fails, __getattr__ is called. So why are there two different methods doing the same thing? If my code implements the new style classes, what should I use?

I am looking for some code examples to clear this question. I have Googled to best of my ability, but the answers that I found don’t discuss the problem thoroughly.

If there is any documentation, I am ready to read that.


回答 0

首先了解一些基础知识。

对于对象,您需要处理其属性。通常我们会这么做instance.attribute。有时我们需要更多的控制权(当我们事先不知道属性名称时)。

例如,instance.attribute将变为getattr(instance, attribute_name)。使用此模型,我们可以通过提供attribute_name作为字符串来获取属性。

用于 __getattr__

您还可以告诉类如何处理它未显式管理的属性,并通过__getattr__方法进行操作。

每当您请求尚未定义的属性时,Python都会调用此方法,因此您可以定义该方法。

一个经典的用例:

class A(dict):
    def __getattr__(self, name):
       return self[name]
a = A()
# Now a.somekey will give a['somekey']

注意事项和使用 __getattribute__

如果您需要捕获每个属性(无论是否存在),请使用__getattribute__。不同之处在于,__getattr__仅调用实际上不存在的属性。如果您直接设置属性,则引用该属性将无需调用即可检索它__getattr__

__getattribute__ 一直被称为。

Some basics first.

With objects, you need to deal with its attributes. Ordinarily we do instance.attribute. Sometimes we need more control (when we do not know the name of the attribute in advance).

For example, instance.attribute would become getattr(instance, attribute_name). Using this model, we can get the attribute by supplying the attribute_name as a string.

Use of __getattr__

You can also tell a class how to deal with attributes which it doesn’t explicitly manage and do that via __getattr__ method.

Python will call this method whenever you request an attribute that hasn’t already been defined, so you can define what to do with it.

A classic use case:

class A(dict):
    def __getattr__(self, name):
       return self[name]
a = A()
# Now a.somekey will give a['somekey']

Caveats and use of __getattribute__

If you need to catch every attribute regardless whether it exists or not, use __getattribute__ instead. The difference is that __getattr__ only gets called for attributes that don’t actually exist. If you set an attribute directly, referencing that attribute will retrieve it without calling __getattr__.

__getattribute__ is called all the times.


回答 1

__getattribute__ 每当发生属性访问时都会调用。

class Foo(object):
    def __init__(self, a):
        self.a = 1

    def __getattribute__(self, attr):
        try:
            return self.__dict__[attr]
        except KeyError:
            return 'default'
f = Foo(1)
f.a

这将导致无限递归。罪魁祸首是排队return self.__dict__[attr]。让我们假装(这与事实很接近)所有属性都存储在self.__dict__名称中并可用。线

f.a

尝试访问的a属性f。这叫f.__getattribute__('a')__getattribute__然后尝试加载self.__dict____dict__是的属性,self == f因此python调用f.__getattribute__('__dict__')再次尝试访问属性'__dict__‘。这是无限递归。

如果__getattr__曾经使用过,那么

  1. 它永远不会运行,因为f具有a属性。
  2. 如果它已经运行((假设您要f.b)),则不会调用__dict__它,因为它已经存在,并且__getattr__仅当所有其他查找属性的方法均失败时才被调用。

编写上述类的“正确”方法__getattribute__

class Foo(object):
    # Same __init__

    def __getattribute__(self, attr):
        return super(Foo, self).__getattribute__(attr)

super(Foo, self).__getattribute__(attr)__getattribute__“最近”超类的方法(self通常是该类的“方法解析顺序”中的下一个类)绑定到当前对象,然后调用它并让其完成工作。

通过使用__getattr__python 可以在没有找到属性之前将其正常处理,从而避免了所有这些麻烦。到那时,Python将控制权交给您的__getattr__方法,并让它提出一些建议。

还值得注意的是,您可以使用进行无限递归__getattr__

class Foo(object):
    def __getattr__(self, attr):
        return self.attr

我将把它留作练习。

__getattribute__ is called whenever an attribute access occurs.

class Foo(object):
    def __init__(self, a):
        self.a = 1

    def __getattribute__(self, attr):
        try:
            return self.__dict__[attr]
        except KeyError:
            return 'default'
f = Foo(1)
f.a

This will cause infinite recursion. The culprit here is the line return self.__dict__[attr]. Let’s pretend (It’s close enough to the truth) that all attributes are stored in self.__dict__ and available by their name. The line

f.a

attempts to access the a attribute of f. This calls f.__getattribute__('a'). __getattribute__ then tries to load self.__dict__. __dict__ is an attribute of self == f and so python calls f.__getattribute__('__dict__') which again tries to access the attribute '__dict__‘. This is infinite recursion.

If __getattr__ had been used instead then

  1. It never would have run because f has an a attribute.
  2. If it had run, (let’s say that you asked for f.b) then it would not have been called to find __dict__ because it’s already there and __getattr__ is invoked only if all other methods of finding the attribute have failed.

The ‘correct’ way to write the above class using __getattribute__ is

class Foo(object):
    # Same __init__

    def __getattribute__(self, attr):
        return super(Foo, self).__getattribute__(attr)

super(Foo, self).__getattribute__(attr) binds the __getattribute__ method of the ‘nearest’ superclass (formally, the next class in the class’s Method Resolution Order, or MRO) to the current object self and then calls it and lets that do the work.

All of this trouble is avoided by using __getattr__ which lets Python do it’s normal thing until an attribute isn’t found. At that point, Python hands control over to your __getattr__ method and lets it come up with something.

It’s also worth noting that you can run into infinite recursion with __getattr__.

class Foo(object):
    def __getattr__(self, attr):
        return self.attr

I’ll leave that one as an exercise.


回答 2

我认为其他的答案做了解释之间的差异的一个伟大的工作__getattr____getattribute__,但有一点可能没有明确的是,为什么你会想使用__getattribute__。有趣的__getattribute__是,它本质上允许您在访问类时重载点。这使您可以自定义如何在较低级别访问属性。例如,假设我要定义一个类,其中所有仅带有自变量的方法都被视为属性:

# prop.py
import inspect

class PropClass(object):
    def __getattribute__(self, attr):
        val = super(PropClass, self).__getattribute__(attr)
        if callable(val):
            argcount = len(inspect.getargspec(val).args)
            # Account for self
            if argcount == 1:
                return val()
            else:
                return val
        else:
            return val

从交互式解释器中:

>>> import prop
>>> class A(prop.PropClass):
...     def f(self):
...             return 1
... 
>>> a = A()
>>> a.f
1

当然,这是一个愚蠢的示例,您可能永远也不想这样做,但是它向您展示了从覆盖获得的强大功能__getattribute__

I think the other answers have done a great job of explaining the difference between __getattr__ and __getattribute__, but one thing that might not be clear is why you would want to use __getattribute__. The cool thing about __getattribute__ is that it essentially allows you to overload the dot when accessing a class. This allows you to customize how attributes are accessed at a low level. For instance, suppose I want to define a class where all methods that only take a self argument are treated as properties:

# prop.py
import inspect

class PropClass(object):
    def __getattribute__(self, attr):
        val = super(PropClass, self).__getattribute__(attr)
        if callable(val):
            argcount = len(inspect.getargspec(val).args)
            # Account for self
            if argcount == 1:
                return val()
            else:
                return val
        else:
            return val

And from the interactive interpreter:

>>> import prop
>>> class A(prop.PropClass):
...     def f(self):
...             return 1
... 
>>> a = A()
>>> a.f
1

Of course this is a silly example and you probably wouldn’t ever want to do this, but it shows you the power you can get from overriding __getattribute__.


回答 3

我经历了别人的出色解释。但是,我从此博客Python Magic Methods和__getattr__找到了一个简单的答案。以下所有都是从那里开始的。

使用__getattr__magic方法,我们可以拦截不存在的属性查找并做一些事情,以确保它不会失败:

class Dummy(object):

    def __getattr__(self, attr):
        return attr.upper()

d = Dummy()
d.does_not_exist # 'DOES_NOT_EXIST'
d.what_about_this_one  # 'WHAT_ABOUT_THIS_ONE'

但是,如果该属性确实存在,__getattr__将不会被调用:

class Dummy(object):

    def __getattr__(self, attr):
        return attr.upper()

d = Dummy()
d.value = "Python"
print(d.value)  # "Python"

__getattribute__与相似__getattr__,但重要的区别是__getattribute__将拦截每个属性查找,而属性是否存在无关紧要。

class Dummy(object):

    def __getattribute__(self, attr):
        return 'YOU SEE ME?'

d = Dummy()
d.value = "Python"
print(d.value)  # "YOU SEE ME?"

在该示例中,d对象已经具有属性值。但是,当我们尝试访问它时,没有得到原始的期望值(“ Python”);我们只是得到任何__getattribute__回报。这意味着我们实际上失去了value属性;它已经变得“无法到达”。

I have gone through other’s excellent explanation. However, I found a simple answer from this blog Python Magic Methods and __getattr__. All the following are from there.

Using the __getattr__ magic method, we can intercept that inexistent attribute lookup and do something so it doesn’t fail:

class Dummy(object):

    def __getattr__(self, attr):
        return attr.upper()

d = Dummy()
d.does_not_exist # 'DOES_NOT_EXIST'
d.what_about_this_one  # 'WHAT_ABOUT_THIS_ONE'

But if the attribute does exist, __getattr__ won’t be invoked:

class Dummy(object):

    def __getattr__(self, attr):
        return attr.upper()

d = Dummy()
d.value = "Python"
print(d.value)  # "Python"

__getattribute__ is similar to __getattr__, with the important difference that __getattribute__ will intercept EVERY attribute lookup, doesn’t matter if the attribute exists or not.

class Dummy(object):

    def __getattribute__(self, attr):
        return 'YOU SEE ME?'

d = Dummy()
d.value = "Python"
print(d.value)  # "YOU SEE ME?"

In that example, the d object already has an attribute value. But when we try to access it, we don’t get the original expected value (“Python”); we’re just getting whatever __getattribute__ returned. It means that we’ve virtually lost the value attribute; it has become “unreachable”.


为什么Python代码使用len()函数而不是length方法?

问题:为什么Python代码使用len()函数而不是length方法?

我知道python具有len()用于确定字符串大小的函数,但是我想知道为什么它不是字符串对象的方法。

更新资料

好吧,我意识到我很尴尬地犯了错误。__len__()实际上是字符串对象的方法。在字符串对象上使用len函数在Python中看到面向对象的代码似乎很奇怪。此外,看到__len__名字而不是len 也很奇怪。

I know that python has a len() function that is used to determine the size of a string, but I was wondering why it’s not a method of the string object.

Update

Ok, I realized I was embarrassingly mistaken. __len__() is actually a method of a string object. It just seems weird to see object oriented code in Python using the len function on string objects. Furthermore, it’s also weird to see __len__ as the name instead of just len.


回答 0

字符串确实有一个length方法: __len__()

Python中的协议是在具有一定长度并使用内置len()函数的对象上实现此方法,该内置函数会为您调用该方法,类似于您实现__iter__()和使用内置iter()函数的方法(或在后面调用方法)的场景)在可迭代的对象上。

有关更多信息,请参见模拟容器类型

这是有关Python协议主题的好书:Python和最小惊讶原则

Strings do have a length method: __len__()

The protocol in Python is to implement this method on objects which have a length and use the built-in len() function, which calls it for you, similar to the way you would implement __iter__() and use the built-in iter() function (or have the method called behind the scenes for you) on objects which are iterable.

See Emulating container types for more information.

Here’s a good read on the subject of protocols in Python: Python and the Principle of Least Astonishment


回答 1

吉姆对这个问题的回答可能会有所帮助。我在这里复制。引用Guido van Rossum:

首先,出于HCI的原因,我选择len(x)而不是x.len()(def __len __()来得很晚)。实际上,两个HCI相互交织在一起:

(a)对于某些运算,前缀表示法比后缀读得更好-前缀(和infix!)运算符在数学中有很长的传统,喜欢在视觉上帮助数学家思考问题的表示法。将我们将x *(a + b)之类的公式重写为x a + x b 的简便性与使用原始OO符号做相同事情的笨拙性进行比较。

(b)当我读到说len(x)的代码时,我知道它是在问某物的长度。这告诉我两件事:结果是整数,参数是某种容器。相反,当我阅读x.len()时,我必须已经知道x是某种实现接口或从具有标准len()的类继承的容器。当未实现映射的类具有get()或keys()方法,或者非文件类具有write()方法时,我们有时会感到困惑。

用另一种方式说同样的事情,我将“ len”视为内置操作。我不想失去那个。/…/

Jim’s answer to this question may help; I copy it here. Quoting Guido van Rossum:

First of all, I chose len(x) over x.len() for HCI reasons (def __len__() came much later). There are two intertwined reasons actually, both HCI:

(a) For some operations, prefix notation just reads better than postfix — prefix (and infix!) operations have a long tradition in mathematics which likes notations where the visuals help the mathematician thinking about a problem. Compare the easy with which we rewrite a formula like x*(a+b) into xa + xb to the clumsiness of doing the same thing using a raw OO notation.

(b) When I read code that says len(x) I know that it is asking for the length of something. This tells me two things: the result is an integer, and the argument is some kind of container. To the contrary, when I read x.len(), I have to already know that x is some kind of container implementing an interface or inheriting from a class that has a standard len(). Witness the confusion we occasionally have when a class that is not implementing a mapping has a get() or keys() method, or something that isn’t a file has a write() method.

Saying the same thing in another way, I see ‘len‘ as a built-in operation. I’d hate to lose that. /…/


回答 2

有一种len方法:

>>> a = 'a string of some length'
>>> a.__len__()
23
>>> a.__len__
<method-wrapper '__len__' of str object at 0x02005650>

There is a len method:

>>> a = 'a string of some length'
>>> a.__len__()
23
>>> a.__len__
<method-wrapper '__len__' of str object at 0x02005650>

回答 3

Python是一种务实的编程语言,并为原因len()是一个功能,而不是一个方法strlistdict等务实。

len()内置函数直接处理的内置类型:CPython的执行len()实际返回的值ob_size字段中PyVarObject的C结构代表任意可变大小的内置存储器中的对象。这是很多比调用一个方法快-无属性的查找需要发生。获取集合中的项目数是一种常见的操作,必须对这些基本类型多样为提高工作效率strlistarray.array等。

但是,为了提高一致性,当应用len(o)到用户定义的类型时,Python会o.__len__()作为后备调用。 __len____abs__和所有其他特殊的记录方法的Python数据模型可以很容易地创建对象,其行为像内置插件,使表现力和高度一致的API,我们称之为“Python化”。

通过实现特殊的方法,您的对象可以支持迭代,重载infix运算符,在with块中管理上下文等。您可以将数据模型视为一种使用Python语言本身作为框架的方式,您可以在其中无缝集成所创建的对象。

第二个原因,通过报价从吉多·范罗苏姆等支撑这一个,是它更容易阅读和写len(s)s.len()

该表示法len(s)与带有前缀表示法的一元运算符一致,例如abs(n)len()的使用频率比更高abs(),并且应该易于编写。

可能还有一个历史原因:在Python之前的ABC语言中(在其设计中很有影响力),有一个一元运算符,#s其含义为len(s)

Python is a pragmatic programming language, and the reasons for len() being a function and not a method of str, list, dict etc. are pragmatic.

The len() built-in function deals directly with built-in types: the CPython implementation of len() actually returns the value of the ob_size field in the PyVarObject C struct that represents any variable-sized built-in object in memory. This is much faster than calling a method — no attribute lookup needs to happen. Getting the number of items in a collection is a common operation and must work efficiently for such basic and diverse types as str, list, array.array etc.

However, to promote consistency, when applying len(o) to a user-defined type, Python calls o.__len__() as a fallback. __len__, __abs__ and all the other special methods documented in the Python Data Model make it easy to create objects that behave like the built-ins, enabling the expressive and highly consistent APIs we call “Pythonic”.

By implementing special methods your objects can support iteration, overload infix operators, manage contexts in with blocks etc. You can think of the Data Model as a way of using the Python language itself as a framework where the objects you create can be integrated seamlessly.

A second reason, supported by quotes from Guido van Rossum like this one, is that it is easier to read and write len(s) than s.len().

The notation len(s) is consistent with unary operators with prefix notation, like abs(n). len() is used way more often than abs(), and it deserves to be as easy to write.

There may also be a historical reason: in the ABC language which preceded Python (and was very influential in its design), there was a unary operator written as #s which meant len(s).


回答 4

met% python -c 'import this' | grep 'only one'
There should be one-- and preferably only one --obvious way to do it.
met% python -c 'import this' | grep 'only one'
There should be one-- and preferably only one --obvious way to do it.

回答 5

这里有一些很好的答案,因此在我给出自己的名字之前,我想重点介绍一下我在这里读过的一些宝石(无红宝石双关语)。

  • Python并不是纯粹的OOP语言,它是一种通用的多范式语言,它使程序员能够使用他们最熟悉的范式和/或最适合其解决方案的范式。
  • Python具有一流的功能,因此len实际上是一个对象。另一方面,Ruby没有一流的功能。因此,len函数对象具有自己的方法,可以通过运行进行检查dir(len)

如果您不喜欢此代码在自己的代码中的工作方式,那么使用首选方法重新实现容器是很简单的(请参见下面的示例)。

>>> class List(list):
...     def len(self):
...         return len(self)
...
>>> class Dict(dict):
...     def len(self):
...         return len(self)
...
>>> class Tuple(tuple):
...     def len(self):
...         return len(self)
...
>>> class Set(set):
...     def len(self):
...         return len(self)
...
>>> my_list = List([1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'])
>>> my_dict = Dict({'key': 'value', 'site': 'stackoverflow'})
>>> my_set = Set({1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'})
>>> my_tuple = Tuple((1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'))
>>> my_containers = Tuple((my_list, my_dict, my_set, my_tuple))
>>>
>>> for container in my_containers:
...     print container.len()
...
15
2
15
15

There are some great answers here, and so before I give my own I’d like to highlight a few of the gems (no ruby pun intended) I’ve read here.

  • Python is not a pure OOP language — it’s a general purpose, multi-paradigm language that allows the programmer to use the paradigm they are most comfortable with and/or the paradigm that is best suited for their solution.
  • Python has first-class functions, so len is actually an object. Ruby, on the other hand, doesn’t have first class functions. So the len function object has it’s own methods that you can inspect by running dir(len).

If you don’t like the way this works in your own code, it’s trivial for you to re-implement the containers using your preferred method (see example below).

>>> class List(list):
...     def len(self):
...         return len(self)
...
>>> class Dict(dict):
...     def len(self):
...         return len(self)
...
>>> class Tuple(tuple):
...     def len(self):
...         return len(self)
...
>>> class Set(set):
...     def len(self):
...         return len(self)
...
>>> my_list = List([1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'])
>>> my_dict = Dict({'key': 'value', 'site': 'stackoverflow'})
>>> my_set = Set({1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'})
>>> my_tuple = Tuple((1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'))
>>> my_containers = Tuple((my_list, my_dict, my_set, my_tuple))
>>>
>>> for container in my_containers:
...     print container.len()
...
15
2
15
15

回答 6

你也可以说

>> x = 'test'
>> len(x)
4

使用Python 2.7.3。

You can also say

>> x = 'test'
>> len(x)
4

Using Python 2.7.3.


回答 7

这里的其余答案缺少一些内容:len函数检查__len__方法是否返回非负数intlen作为函数的事实意味着类无法重写此行为以避免检查。因此,len(obj)给出了不能达到的安全级别obj.len()

例:

>>> class A:
...     def __len__(self):
...         return 'foo'
...
>>> len(A())
Traceback (most recent call last):
  File "<pyshell#8>", line 1, in <module>
    len(A())
TypeError: 'str' object cannot be interpreted as an integer
>>> class B:
...     def __len__(self):
...         return -1
... 
>>> len(B())
Traceback (most recent call last):
  File "<pyshell#13>", line 1, in <module>
    len(B())
ValueError: __len__() should return >= 0

当然,可以len通过将其重新分配为全局变量来“覆盖” 函数,但是比起覆盖类中方法的代码,这样做的代码明显更可疑。

Something missing from the rest of the answers here: the len function checks that the __len__ method returns a non-negative int. The fact that len is a function means that classes cannot override this behaviour to avoid the check. As such, len(obj) gives a level of safety that obj.len() cannot.

Example:

>>> class A:
...     def __len__(self):
...         return 'foo'
...
>>> len(A())
Traceback (most recent call last):
  File "<pyshell#8>", line 1, in <module>
    len(A())
TypeError: 'str' object cannot be interpreted as an integer
>>> class B:
...     def __len__(self):
...         return -1
... 
>>> len(B())
Traceback (most recent call last):
  File "<pyshell#13>", line 1, in <module>
    len(B())
ValueError: __len__() should return >= 0

Of course, it is possible to “override” the len function by reassigning it as a global variable, but code which does this is much more obviously suspicious than code which overrides a method in a class.


回答 8

不是吗

>>> "abc".__len__()
3

It doesn’t?

>>> "abc".__len__()
3

如何获得Python类的父母?

问题:如何获得Python类的父母?

如何获得Python类的父类?

How can I get the parent class(es) of a Python class?


回答 0

使用以下属性:

cls.__bases__

文档

类对象的基类的元组。

例:

>>> str.__bases__
(<type 'basestring'>,)

另一个例子:

>>> class A(object):
...   pass
... 
>>> class B(object):
...   pass
... 
>>> class C(A, B):
...   pass
... 
>>> C.__bases__
(<class '__main__.A'>, <class '__main__.B'>)

Use the following attribute:

cls.__bases__

From the docs:

The tuple of base classes of a class object.

Example:

>>> str.__bases__
(<type 'basestring'>,)

Another example:

>>> class A(object):
...   pass
... 
>>> class B(object):
...   pass
... 
>>> class C(A, B):
...   pass
... 
>>> C.__bases__
(<class '__main__.A'>, <class '__main__.B'>)

回答 1

如果要所有祖先而不是直接祖先,请使用inspect.getmro

import inspect
print inspect.getmro(cls)

有用的是,这为您提供了“方法解析顺序”中的所有祖先类-即在解析方法(或实际上是任何其他属性时,将检查祖先的顺序)-方法和其他属性都位于同一个命名空间中毕竟是在Python中;-)。

If you want all the ancestors rather than just the immediate ones, use inspect.getmro:

import inspect
print inspect.getmro(cls)

Usefully, this gives you all ancestor classes in the “method resolution order” — i.e. the order in which the ancestors will be checked when resolving a method (or, actually, any other attribute — methods and other attributes live in the same namespace in Python, after all;-).


回答 2

新型类具有可调用的mro方法,该方法按方法解析顺序返回父类列表。

New-style classes have an mro method you can call which returns a list of parent classes in method resolution order.


回答 3

最快的方式查看所有父母,然后按顺序使用内置__mro__

repr(YOUR_CLASS.__mro__)


>>>
>>>
>>> import getpass
>>> getpass.GetPassWarning.__mro__

输出,IN ORDER


(<class 'getpass.GetPassWarning'>, <type 'exceptions.UserWarning'>,
<type 'exceptions.Warning'>, <type 'exceptions.Exception'>, 
<type 'exceptions.BaseException'>, <type 'object'>)
>>>

你有它。目前“最佳”的答案是182票(我在输入时),但这比一些复杂的for循环要简单得多,一次查看一个类,更不用说当一个类扩展两个或两个以上父级时类。导入和使用inspect仅会不必要地覆盖范围。老实说,人们不知道仅使用内置功能是一种耻辱

我希望这有帮助!

The FASTEST way, to see all parents, and IN ORDER, just use the built in __mro__

i.e. repr(YOUR_CLASS.__mro__)


>>>
>>>
>>> import getpass
>>> getpass.GetPassWarning.__mro__

outputs, IN ORDER


(<class 'getpass.GetPassWarning'>, <type 'exceptions.UserWarning'>,
<type 'exceptions.Warning'>, <type 'exceptions.Exception'>, 
<type 'exceptions.BaseException'>, <type 'object'>)
>>>

There you have it. The “best” answer right now, has 182 votes (as I am typing this) but this is SO much simpler than some convoluted for loop, looking into bases one class at a time, not to mention when a class extends TWO or more parent classes. Importing and using inspect just clouds the scope unnecessarily. It honestly is a shame people don’t know to just use the built-ins

I Hope this Helps!


回答 4

如果您只想获取父母,请使用基数,使用__mro__(如@ naught101所指出的)获取方法解析顺序(以便知道初始化的执行顺序)。

基础(首先获取现有对象的类):

>>> some_object = "some_text"
>>> some_object.__class__.__bases__
(object,)

对于最新Python版本中的mro:

>>> some_object = "some_text"
>>> some_object.__class__.__mro__
(str, object)

显然,当您已经有了一个类定义时,您可以直接调用__mro__它:

>>> class A(): pass
>>> A.__mro__
(__main__.A, object)

Use bases if you just want to get the parents, use __mro__ (as pointed out by @naught101) for getting the method resolution order (so to know in which order the init’s were executed).

Bases (and first getting the class for an existing object):

>>> some_object = "some_text"
>>> some_object.__class__.__bases__
(object,)

For mro in recent Python versions:

>>> some_object = "some_text"
>>> some_object.__class__.__mro__
(str, object)

Obviously, when you already have a class definition, you can just call __mro__ on that directly:

>>> class A(): pass
>>> A.__mro__
(__main__.A, object)

回答 5

如果要确保它们都被调用,请super在所有级别上使用。

If you want to ensure they all get called, use super at all levels.