标签归档:syntax

如何使用点“。” 访问字典成员?

问题:如何使用点“。” 访问字典成员?

如何通过点“。”访问Python词典成员?

例如,mydict['val']我不想写,而是想写mydict.val

我也想以这种方式访问​​嵌套的字典。例如

mydict.mydict2.val 

将指

mydict = { 'mydict2': { 'val': ... } }

How do I make Python dictionary members accessible via a dot “.”?

For example, instead of writing mydict['val'], I’d like to write mydict.val.

Also I’d like to access nested dicts this way. For example

mydict.mydict2.val 

would refer to

mydict = { 'mydict2': { 'val': ... } }

回答 0

您可以使用我刚刚制作的此类进行操作。通过此类,您可以Map像其他字典(包括json序列化)一样使用该对象,也可以使用点符号。希望对您有所帮助:

class Map(dict):
    """
    Example:
    m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
    """
    def __init__(self, *args, **kwargs):
        super(Map, self).__init__(*args, **kwargs)
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.iteritems():
                    self[k] = v

        if kwargs:
            for k, v in kwargs.iteritems():
                self[k] = v

    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(Map, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(Map, self).__delitem__(key)
        del self.__dict__[key]

用法示例:

m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
# Add new key
m.new_key = 'Hello world!'
# Or
m['new_key'] = 'Hello world!'
print m.new_key
print m['new_key']
# Update values
m.new_key = 'Yay!'
# Or
m['new_key'] = 'Yay!'
# Delete key
del m.new_key
# Or
del m['new_key']

You can do it using this class I just made. With this class you can use the Map object like another dictionary(including json serialization) or with the dot notation. I hope to help you:

class Map(dict):
    """
    Example:
    m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
    """
    def __init__(self, *args, **kwargs):
        super(Map, self).__init__(*args, **kwargs)
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.iteritems():
                    self[k] = v

        if kwargs:
            for k, v in kwargs.iteritems():
                self[k] = v

    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(Map, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(Map, self).__delitem__(key)
        del self.__dict__[key]

Usage examples:

m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
# Add new key
m.new_key = 'Hello world!'
# Or
m['new_key'] = 'Hello world!'
print m.new_key
print m['new_key']
# Update values
m.new_key = 'Yay!'
# Or
m['new_key'] = 'Yay!'
# Delete key
del m.new_key
# Or
del m['new_key']

回答 1

我一直将其保存在util文件中。您也可以在自己的类中将其用作混合。

class dotdict(dict):
    """dot.notation access to dictionary attributes"""
    __getattr__ = dict.get
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

mydict = {'val':'it works'}
nested_dict = {'val':'nested works too'}
mydict = dotdict(mydict)
mydict.val
# 'it works'

mydict.nested = dotdict(nested_dict)
mydict.nested.val
# 'nested works too'

I’ve always kept this around in a util file. You can use it as a mixin on your own classes too.

class dotdict(dict):
    """dot.notation access to dictionary attributes"""
    __getattr__ = dict.get
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

mydict = {'val':'it works'}
nested_dict = {'val':'nested works too'}
mydict = dotdict(mydict)
mydict.val
# 'it works'

mydict.nested = dotdict(nested_dict)
mydict.nested.val
# 'nested works too'

回答 2

dotmap通过安装pip

pip install dotmap

它可以完成您想要做的所有事情并成为其子类dict,因此它的作用类似于普通字典:

from dotmap import DotMap

m = DotMap()
m.hello = 'world'
m.hello
m.hello += '!'
# m.hello and m['hello'] now both return 'world!'
m.val = 5
m.val2 = 'Sam'

最重要的是,您可以将其与dict对象之间进行转换:

d = m.toDict()
m = DotMap(d) # automatic conversion in constructor

这意味着,如果您要访问的内容已经dict存在,可以将其转换DotMap为易于访问的内容:

import json
jsonDict = json.loads(text)
data = DotMap(jsonDict)
print data.location.city

最后,它会自动创建新的子DotMap实例,因此您可以执行以下操作:

m = DotMap()
m.people.steve.age = 31

与束比较

全面披露:我是DotMap的创建者。我创建它是因为Bunch缺少这些功能

  • 记住添加了订单项并按照该顺序进行迭代
  • 自动子 DotMap创建,这样可以节省时间,并在您具有多个层次结构时使代码更简洁
  • 从构造dict并将所有子dict实例递归转换为DotMap

Install dotmap via pip

pip install dotmap

It does everything you want it to do and subclasses dict, so it operates like a normal dictionary:

from dotmap import DotMap

m = DotMap()
m.hello = 'world'
m.hello
m.hello += '!'
# m.hello and m['hello'] now both return 'world!'
m.val = 5
m.val2 = 'Sam'

On top of that, you can convert it to and from dict objects:

d = m.toDict()
m = DotMap(d) # automatic conversion in constructor

This means that if something you want to access is already in dict form, you can turn it into a DotMap for easy access:

import json
jsonDict = json.loads(text)
data = DotMap(jsonDict)
print data.location.city

Finally, it automatically creates new child DotMap instances so you can do things like this:

m = DotMap()
m.people.steve.age = 31

Comparison to Bunch

Full disclosure: I am the creator of the DotMap. I created it because Bunch was missing these features

  • remembering the order items are added and iterating in that order
  • automatic child DotMap creation, which saves time and makes for cleaner code when you have a lot of hierarchy
  • constructing from a dict and recursively converting all child dict instances to DotMap

回答 3

从指令派生并实施__getattr__和实施__setattr__

或者您可以使用非常相似的Bunch

我认为不可能对内置的dict类进行修补。

Derive from dict and and implement __getattr__ and __setattr__.

Or you can use Bunch which is very similar.

I don’t think it’s possible to monkeypatch built-in dict class.


回答 4

Fabric具有非常好的,最小的实现。扩展它以允许嵌套访问,我们可以使用defaultdict,结果看起来像这样:

from collections import defaultdict

class AttributeDict(defaultdict):
    def __init__(self):
        super(AttributeDict, self).__init__(AttributeDict)

    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(key)

    def __setattr__(self, key, value):
        self[key] = value

如下使用它:

keys = AttributeDict()
keys.abc.xyz.x = 123
keys.abc.xyz.a.b.c = 234

这就详细说明了库格尔的回答:“从命令和实现中得出__getattr____setattr__”。现在您知道了!

Fabric has a really nice, minimal implementation. Extending that to allow for nested access, we can use a defaultdict, and the result looks something like this:

from collections import defaultdict

class AttributeDict(defaultdict):
    def __init__(self):
        super(AttributeDict, self).__init__(AttributeDict)

    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(key)

    def __setattr__(self, key, value):
        self[key] = value

Make use of it as follows:

keys = AttributeDict()
keys.abc.xyz.x = 123
keys.abc.xyz.a.b.c = 234

That elaborates a bit on Kugel’s answer of “Derive from dict and and implement __getattr__ and __setattr__“. Now you know how!


回答 5

我尝试了这个:

class dotdict(dict):
    def __getattr__(self, name):
        return self[name]

你可以试试 __getattribute__

让每个字典都使用点分隔符就足够了,如果您想从多层字典中初始化它,也可以尝试实现__init__

I tried this:

class dotdict(dict):
    def __getattr__(self, name):
        return self[name]

you can try __getattribute__ too.

make every dict a type of dotdict would be good enough, if you want to init this from a multi-layer dict, try implement __init__ too.


回答 6

别。属性访问和索引编制在Python中是分开的,您不希望它们执行相同的操作。namedtuple如果您有一些应该具有可访问属性的东西,并使用[]表示法从字典中获取一项,则创建一个类(可能是由制成的类)。

Don’t. Attribute access and indexing are separate things in Python, and you shouldn’t want them to perform the same. Make a class (possibly one made by namedtuple) if you have something that should have accessible attributes and use [] notation to get an item from a dict.


回答 7

如果要腌制修改后的字典,则需要在上述答案中添加一些状态方法:

class DotDict(dict):
    """dot.notation access to dictionary attributes"""
    def __getattr__(self, attr):
        return self.get(attr)
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__

    def __getstate__(self):
        return self

    def __setstate__(self, state):
        self.update(state)
        self.__dict__ = self

If you want to pickle your modified dictionary, you need to add few state methods to above answers:

class DotDict(dict):
    """dot.notation access to dictionary attributes"""
    def __getattr__(self, attr):
        return self.get(attr)
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__

    def __getstate__(self):
        return self

    def __setstate__(self, state):
        self.update(state)
        self.__dict__ = self

回答 8

以库格尔(Kugel)的答案为基础,并考虑迈克·格雷厄姆(Mike Graham)的警告语,如果我们做包装纸怎么办?

class DictWrap(object):
  """ Wrap an existing dict, or create a new one, and access with either dot 
    notation or key lookup.

    The attribute _data is reserved and stores the underlying dictionary.
    When using the += operator with create=True, the empty nested dict is 
    replaced with the operand, effectively creating a default dictionary
    of mixed types.

    args:
      d({}): Existing dict to wrap, an empty dict is created by default
      create(True): Create an empty, nested dict instead of raising a KeyError

    example:
      >>>dw = DictWrap({'pp':3})
      >>>dw.a.b += 2
      >>>dw.a.b += 2
      >>>dw.a['c'] += 'Hello'
      >>>dw.a['c'] += ' World'
      >>>dw.a.d
      >>>print dw._data
      {'a': {'c': 'Hello World', 'b': 4, 'd': {}}, 'pp': 3}

  """

  def __init__(self, d=None, create=True):
    if d is None:
      d = {}
    supr = super(DictWrap, self)  
    supr.__setattr__('_data', d)
    supr.__setattr__('__create', create)

  def __getattr__(self, name):
    try:
      value = self._data[name]
    except KeyError:
      if not super(DictWrap, self).__getattribute__('__create'):
        raise
      value = {}
      self._data[name] = value

    if hasattr(value, 'items'):
      create = super(DictWrap, self).__getattribute__('__create')
      return DictWrap(value, create)
    return value

  def __setattr__(self, name, value):
    self._data[name] = value  

  def __getitem__(self, key):
    try:
      value = self._data[key]
    except KeyError:
      if not super(DictWrap, self).__getattribute__('__create'):
        raise
      value = {}
      self._data[key] = value

    if hasattr(value, 'items'):
      create = super(DictWrap, self).__getattribute__('__create')
      return DictWrap(value, create)
    return value

  def __setitem__(self, key, value):
    self._data[key] = value

  def __iadd__(self, other):
    if self._data:
      raise TypeError("A Nested dict will only be replaced if it's empty")
    else:
      return other

Building on Kugel’s answer and taking Mike Graham’s words of caution into consideration, what if we make a wrapper?

class DictWrap(object):
  """ Wrap an existing dict, or create a new one, and access with either dot 
    notation or key lookup.

    The attribute _data is reserved and stores the underlying dictionary.
    When using the += operator with create=True, the empty nested dict is 
    replaced with the operand, effectively creating a default dictionary
    of mixed types.

    args:
      d({}): Existing dict to wrap, an empty dict is created by default
      create(True): Create an empty, nested dict instead of raising a KeyError

    example:
      >>>dw = DictWrap({'pp':3})
      >>>dw.a.b += 2
      >>>dw.a.b += 2
      >>>dw.a['c'] += 'Hello'
      >>>dw.a['c'] += ' World'
      >>>dw.a.d
      >>>print dw._data
      {'a': {'c': 'Hello World', 'b': 4, 'd': {}}, 'pp': 3}

  """

  def __init__(self, d=None, create=True):
    if d is None:
      d = {}
    supr = super(DictWrap, self)  
    supr.__setattr__('_data', d)
    supr.__setattr__('__create', create)

  def __getattr__(self, name):
    try:
      value = self._data[name]
    except KeyError:
      if not super(DictWrap, self).__getattribute__('__create'):
        raise
      value = {}
      self._data[name] = value

    if hasattr(value, 'items'):
      create = super(DictWrap, self).__getattribute__('__create')
      return DictWrap(value, create)
    return value

  def __setattr__(self, name, value):
    self._data[name] = value  

  def __getitem__(self, key):
    try:
      value = self._data[key]
    except KeyError:
      if not super(DictWrap, self).__getattribute__('__create'):
        raise
      value = {}
      self._data[key] = value

    if hasattr(value, 'items'):
      create = super(DictWrap, self).__getattribute__('__create')
      return DictWrap(value, create)
    return value

  def __setitem__(self, key, value):
    self._data[key] = value

  def __iadd__(self, other):
    if self._data:
      raise TypeError("A Nested dict will only be replaced if it's empty")
    else:
      return other

回答 9

用途SimpleNamespace

>>> from types import SimpleNamespace   
>>> d = dict(x=[1, 2], y=['a', 'b'])
>>> ns = SimpleNamespace(**d)
>>> ns.x
[1, 2]
>>> ns
namespace(x=[1, 2], y=['a', 'b'])

Use SimpleNamespace:

>>> from types import SimpleNamespace   
>>> d = dict(x=[1, 2], y=['a', 'b'])
>>> ns = SimpleNamespace(**d)
>>> ns.x
[1, 2]
>>> ns
namespace(x=[1, 2], y=['a', 'b'])

回答 10

我喜欢Munch,它在点访问之外还提供了许多方便的选项。

进口午餐

temp_1 = {‘person’:{‘fname’:’senthil’,’lname’:’ramalingam’}}

dict_munch = munch.munchify(temp_1)

dict_munch.person.fname

I like the Munch and it gives lot of handy options on top of dot access.

import munch

temp_1 = {‘person’: { ‘fname’: ‘senthil’, ‘lname’: ‘ramalingam’}}

dict_munch = munch.munchify(temp_1)

dict_munch.person.fname


回答 11

最近,我遇到了“ Box ”库,它做同样的事情。

安装命令: pip install python-box

例:

from box import Box

mydict = {"key1":{"v1":0.375,
                    "v2":0.625},
          "key2":0.125,
          }
mydict = Box(mydict)

print(mydict.key1.v1)

我发现它比其他现有的库(例如dotmap)更有效,如点阵图,当嵌套的字典较大时,它们会生成python递归错误。

链接到库和详细信息:https : //pypi.org/project/python-box/

I recently came across the ‘Box‘ library which does the same thing.

Installation command : pip install python-box

Example:

from box import Box

mydict = {"key1":{"v1":0.375,
                    "v2":0.625},
          "key2":0.125,
          }
mydict = Box(mydict)

print(mydict.key1.v1)

I found it to be more effective than other existing libraries like dotmap, which generate python recursion error when you have large nested dicts.

link to library and details: https://pypi.org/project/python-box/


回答 12

使用__getattr__非常简单,可在Python 3.4.3中使用

class myDict(dict):
    def __getattr__(self,val):
        return self[val]


blockBody=myDict()
blockBody['item1']=10000
blockBody['item2']="StackOverflow"
print(blockBody.item1)
print(blockBody.item2)

输出:

10000
StackOverflow

Use __getattr__, very simple, works in Python 3.4.3

class myDict(dict):
    def __getattr__(self,val):
        return self[val]


blockBody=myDict()
blockBody['item1']=10000
blockBody['item2']="StackOverflow"
print(blockBody.item1)
print(blockBody.item2)

Output:

10000
StackOverflow

回答 13

语言本身不支持此功能,但有时这仍然是有用的要求。除了Bunch食谱之外,您还可以编写一些小方法,该方法可以使用点分字符串来访问字典:

def get_var(input_dict, accessor_string):
    """Gets data from a dictionary using a dotted accessor-string"""
    current_data = input_dict
    for chunk in accessor_string.split('.'):
        current_data = current_data.get(chunk, {})
    return current_data

这将支持以下内容:

>> test_dict = {'thing': {'spam': 12, 'foo': {'cheeze': 'bar'}}}
>> output = get_var(test_dict, 'thing.spam.foo.cheeze')
>> print output
'bar'
>>

The language itself doesn’t support this, but sometimes this is still a useful requirement. Besides the Bunch recipe, you can also write a little method which can access a dictionary using a dotted string:

def get_var(input_dict, accessor_string):
    """Gets data from a dictionary using a dotted accessor-string"""
    current_data = input_dict
    for chunk in accessor_string.split('.'):
        current_data = current_data.get(chunk, {})
    return current_data

which would support something like this:

>> test_dict = {'thing': {'spam': 12, 'foo': {'cheeze': 'bar'}}}
>> output = get_var(test_dict, 'thing.spam.foo.cheeze')
>> print output
'bar'
>>

回答 14

为了建立epool的答案,此版本允许您通过点运算符访问内部的所有dict:

foo = {
    "bar" : {
        "baz" : [ {"boo" : "hoo"} , {"baba" : "loo"} ]
    }
}

例如,foo.bar.baz[1].babareturn "loo"

class Map(dict):
    def __init__(self, *args, **kwargs):
        super(Map, self).__init__(*args, **kwargs)
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.iteritems():
                    if isinstance(v, dict):
                        v = Map(v)
                    if isinstance(v, list):
                        self.__convert(v)
                    self[k] = v

        if kwargs:
            for k, v in kwargs.iteritems():
                if isinstance(v, dict):
                    v = Map(v)
                elif isinstance(v, list):
                    self.__convert(v)
                self[k] = v

    def __convert(self, v):
        for elem in xrange(0, len(v)):
            if isinstance(v[elem], dict):
                v[elem] = Map(v[elem])
            elif isinstance(v[elem], list):
                self.__convert(v[elem])

    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(Map, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(Map, self).__delitem__(key)
        del self.__dict__[key]

To build upon epool’s answer, this version allows you to access any dict inside via the dot operator:

foo = {
    "bar" : {
        "baz" : [ {"boo" : "hoo"} , {"baba" : "loo"} ]
    }
}

For instance, foo.bar.baz[1].baba returns "loo".

class Map(dict):
    def __init__(self, *args, **kwargs):
        super(Map, self).__init__(*args, **kwargs)
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.iteritems():
                    if isinstance(v, dict):
                        v = Map(v)
                    if isinstance(v, list):
                        self.__convert(v)
                    self[k] = v

        if kwargs:
            for k, v in kwargs.iteritems():
                if isinstance(v, dict):
                    v = Map(v)
                elif isinstance(v, list):
                    self.__convert(v)
                self[k] = v

    def __convert(self, v):
        for elem in xrange(0, len(v)):
            if isinstance(v[elem], dict):
                v[elem] = Map(v[elem])
            elif isinstance(v[elem], list):
                self.__convert(v[elem])

    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(Map, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(Map, self).__delitem__(key)
        del self.__dict__[key]

回答 15

def dict_to_object(dick):
    # http://stackoverflow.com/a/1305663/968442

    class Struct:
        def __init__(self, **entries):
            self.__dict__.update(entries)

    return Struct(**dick)

如果决定永久将其转换dict为对象,则应该这样做。您可以在访问之前创建一个一次性对象。

d = dict_to_object(d)
def dict_to_object(dick):
    # http://stackoverflow.com/a/1305663/968442

    class Struct:
        def __init__(self, **entries):
            self.__dict__.update(entries)

    return Struct(**dick)

If one decides to permanently convert that dict to object this should do. You can create a throwaway object just before accessing.

d = dict_to_object(d)

回答 16

我最终都尝试了AttrDict Bunch库,发现它们是我使用速度减慢的方法。经过一个朋友和我的研究,我们发现编写这些库的主要方法导致该库通过嵌套对象积极地递归并在整个字典对象中进行复制。考虑到这一点,我们进行了两个关键更改。1)我们使属性延迟加载; 2)我们创建轻量级代理对象的副本,而不是创建字典对象的副本。这是最终的实现。使用此代码的性能提升令人难以置信。使用AttrDict或Bunch时,仅这两个库分别消耗了我的请求时间的1/2和1/3(什么!?)。这段代码将时间减少到几乎没有(在0.5ms范围内)。当然,这取决于您的需求,但是如果您在代码中大量使用此功能,

class DictProxy(object):
    def __init__(self, obj):
        self.obj = obj

    def __getitem__(self, key):
        return wrap(self.obj[key])

    def __getattr__(self, key):
        try:
            return wrap(getattr(self.obj, key))
        except AttributeError:
            try:
                return self[key]
            except KeyError:
                raise AttributeError(key)

    # you probably also want to proxy important list properties along like
    # items(), iteritems() and __len__

class ListProxy(object):
    def __init__(self, obj):
        self.obj = obj

    def __getitem__(self, key):
        return wrap(self.obj[key])

    # you probably also want to proxy important list properties along like
    # __iter__ and __len__

def wrap(value):
    if isinstance(value, dict):
        return DictProxy(value)
    if isinstance(value, (tuple, list)):
        return ListProxy(value)
    return value

通过https://stackoverflow.com/users/704327/michael-merickel查看此处的原始实现。

还要注意的另一件事是,此实现非常简单,并没有实现您可能需要的所有方法。您需要根据需要在DictProxy或ListProxy对象上编写这些内容。

I ended up trying BOTH the AttrDict and the Bunch libraries and found them to be way to slow for my uses. After a friend and I looked into it, we found that the main method for writing these libraries results in the library aggressively recursing through a nested object and making copies of the dictionary object throughout. With this in mind, we made two key changes. 1) We made attributes lazy-loaded 2) instead of creating copies of a dictionary object, we create copies of a light-weight proxy object. This is the final implementation. The performance increase of using this code is incredible. When using AttrDict or Bunch, these two libraries alone consumed 1/2 and 1/3 respectively of my request time(what!?). This code reduced that time to almost nothing(somewhere in the range of 0.5ms). This of course depends on your needs, but if you are using this functionality quite a bit in your code, definitely go with something simple like this.

class DictProxy(object):
    def __init__(self, obj):
        self.obj = obj

    def __getitem__(self, key):
        return wrap(self.obj[key])

    def __getattr__(self, key):
        try:
            return wrap(getattr(self.obj, key))
        except AttributeError:
            try:
                return self[key]
            except KeyError:
                raise AttributeError(key)

    # you probably also want to proxy important list properties along like
    # items(), iteritems() and __len__

class ListProxy(object):
    def __init__(self, obj):
        self.obj = obj

    def __getitem__(self, key):
        return wrap(self.obj[key])

    # you probably also want to proxy important list properties along like
    # __iter__ and __len__

def wrap(value):
    if isinstance(value, dict):
        return DictProxy(value)
    if isinstance(value, (tuple, list)):
        return ListProxy(value)
    return value

See the original implementation here by https://stackoverflow.com/users/704327/michael-merickel.

The other thing to note, is that this implementation is pretty simple and doesn’t implement all of the methods you might need. You’ll need to write those as required on the DictProxy or ListProxy objects.


回答 17

我想将自己的解决方案付诸实践:

https://github.com/skorokithakis/jsane

它使您可以将JSON解析为可以访问的内容with.attribute.lookups.like.this.r(),主要是因为在开始使用JSON 之前我还没有看到这个答案。

I’d like to throw my own solution into the ring:

https://github.com/skorokithakis/jsane

It allows you to parse JSON into something you can access with.attribute.lookups.like.this.r(), mostly because I hadn’t seen this answer before starting to work on it.


回答 18

不是直接回答OP的问题,而是受到某些人的启发,也许对某些人有用。.我已经使用内部__dict__方法创建了基于对象的解决方案(绝不优化代码)

payload = {
    "name": "John",
    "location": {
        "lat": 53.12312312,
        "long": 43.21345112
    },
    "numbers": [
        {
            "role": "home",
            "number": "070-12345678"
        },
        {
            "role": "office",
            "number": "070-12345679"
        }
    ]
}


class Map(object):
    """
    Dot style access to object members, access raw values
    with an underscore e.g.

    class Foo(Map):
        def foo(self):
            return self.get('foo') + 'bar'

    obj = Foo(**{'foo': 'foo'})

    obj.foo => 'foobar'
    obj._foo => 'foo'

    """

    def __init__(self, *args, **kwargs):
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.iteritems():
                    self.__dict__[k] = v
                    self.__dict__['_' + k] = v

        if kwargs:
            for k, v in kwargs.iteritems():
                self.__dict__[k] = v
                self.__dict__['_' + k] = v

    def __getattribute__(self, attr):
        if hasattr(self, 'get_' + attr):
            return object.__getattribute__(self, 'get_' + attr)()
        else:
            return object.__getattribute__(self, attr)

    def get(self, key):
        try:
            return self.__dict__.get('get_' + key)()
        except (AttributeError, TypeError):
            return self.__dict__.get(key)

    def __repr__(self):
        return u"<{name} object>".format(
            name=self.__class__.__name__
        )


class Number(Map):
    def get_role(self):
        return self.get('role')

    def get_number(self):
        return self.get('number')


class Location(Map):
    def get_latitude(self):
        return self.get('lat') + 1

    def get_longitude(self):
        return self.get('long') + 1


class Item(Map):
    def get_name(self):
        return self.get('name') + " Doe"

    def get_location(self):
        return Location(**self.get('location'))

    def get_numbers(self):
        return [Number(**n) for n in self.get('numbers')]


# Tests

obj = Item({'foo': 'bar'}, **payload)

assert type(obj) == Item
assert obj._name == "John"
assert obj.name == "John Doe"
assert type(obj.location) == Location
assert obj.location._lat == 53.12312312
assert obj.location._long == 43.21345112
assert obj.location.latitude == 54.12312312
assert obj.location.longitude == 44.21345112

for n in obj.numbers:
    assert type(n) == Number
    if n.role == 'home':
        assert n.number == "070-12345678"
    if n.role == 'office':
        assert n.number == "070-12345679"

Not a direct answer to the OP’s question, but inspired by and perhaps useful for some.. I’ve created an object-based solution using the internal __dict__ (In no way optimized code)

payload = {
    "name": "John",
    "location": {
        "lat": 53.12312312,
        "long": 43.21345112
    },
    "numbers": [
        {
            "role": "home",
            "number": "070-12345678"
        },
        {
            "role": "office",
            "number": "070-12345679"
        }
    ]
}


class Map(object):
    """
    Dot style access to object members, access raw values
    with an underscore e.g.

    class Foo(Map):
        def foo(self):
            return self.get('foo') + 'bar'

    obj = Foo(**{'foo': 'foo'})

    obj.foo => 'foobar'
    obj._foo => 'foo'

    """

    def __init__(self, *args, **kwargs):
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.iteritems():
                    self.__dict__[k] = v
                    self.__dict__['_' + k] = v

        if kwargs:
            for k, v in kwargs.iteritems():
                self.__dict__[k] = v
                self.__dict__['_' + k] = v

    def __getattribute__(self, attr):
        if hasattr(self, 'get_' + attr):
            return object.__getattribute__(self, 'get_' + attr)()
        else:
            return object.__getattribute__(self, attr)

    def get(self, key):
        try:
            return self.__dict__.get('get_' + key)()
        except (AttributeError, TypeError):
            return self.__dict__.get(key)

    def __repr__(self):
        return u"<{name} object>".format(
            name=self.__class__.__name__
        )


class Number(Map):
    def get_role(self):
        return self.get('role')

    def get_number(self):
        return self.get('number')


class Location(Map):
    def get_latitude(self):
        return self.get('lat') + 1

    def get_longitude(self):
        return self.get('long') + 1


class Item(Map):
    def get_name(self):
        return self.get('name') + " Doe"

    def get_location(self):
        return Location(**self.get('location'))

    def get_numbers(self):
        return [Number(**n) for n in self.get('numbers')]


# Tests

obj = Item({'foo': 'bar'}, **payload)

assert type(obj) == Item
assert obj._name == "John"
assert obj.name == "John Doe"
assert type(obj.location) == Location
assert obj.location._lat == 53.12312312
assert obj.location._long == 43.21345112
assert obj.location.latitude == 54.12312312
assert obj.location.longitude == 44.21345112

for n in obj.numbers:
    assert type(n) == Number
    if n.role == 'home':
        assert n.number == "070-12345678"
    if n.role == 'office':
        assert n.number == "070-12345679"

回答 19

获得点访问(而不是数组访问)的一种简单方法是在Python中使用普通对象。像这样:

class YourObject:
    def __init__(self, *args, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)

…并像这样使用它:

>>> obj = YourObject(key="value")
>>> print(obj.key)
"value"

…将其转换为字典:

>>> print(obj.__dict__)
{"key": "value"}

One simple way to get dot access (but not array access), is to use a plain object in Python. Like this:

class YourObject:
    def __init__(self, *args, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)

…and use it like this:

>>> obj = YourObject(key="value")
>>> print(obj.key)
"value"

… to convert it to a dict:

>>> print(obj.__dict__)
{"key": "value"}

回答 20

此解决方案是对epool提供的解决方案的改进,以解决OP以一致方式访问嵌套dict的要求。epool的解决方案不允许访问嵌套字典。

class YAMLobj(dict):
    def __init__(self, args):
        super(YAMLobj, self).__init__(args)
        if isinstance(args, dict):
            for k, v in args.iteritems():
                if not isinstance(v, dict):
                    self[k] = v
                else:
                    self.__setattr__(k, YAMLobj(v))


    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(YAMLobj, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(YAMLobj, self).__delitem__(key)
        del self.__dict__[key]

有了这一堂课,您现在可以执行以下操作:A.B.C.D

This solution is a refinement upon the one offered by epool to address the requirement of the OP to access nested dicts in a consistent manner. The solution by epool did not allow for accessing nested dicts.

class YAMLobj(dict):
    def __init__(self, args):
        super(YAMLobj, self).__init__(args)
        if isinstance(args, dict):
            for k, v in args.iteritems():
                if not isinstance(v, dict):
                    self[k] = v
                else:
                    self.__setattr__(k, YAMLobj(v))


    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(YAMLobj, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(YAMLobj, self).__delitem__(key)
        del self.__dict__[key]

With this class, one can now do something like: A.B.C.D.


回答 21

这也适用于嵌套字典,并确保稍后附加的字典具有相同的行为:

class DotDict(dict):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Recursively turn nested dicts into DotDicts
        for key, value in self.items():
            if type(value) is dict:
                self[key] = DotDict(value)

    def __setitem__(self, key, item):
        if type(item) is dict:
            item = DotDict(item)
        super().__setitem__(key, item)

    __setattr__ = __setitem__
    __getattr__ = dict.__getitem__

This also works with nested dicts and makes sure that dicts which are appended later behave the same:

class DotDict(dict):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Recursively turn nested dicts into DotDicts
        for key, value in self.items():
            if type(value) is dict:
                self[key] = DotDict(value)

    def __setitem__(self, key, item):
        if type(item) is dict:
            item = DotDict(item)
        super().__setitem__(key, item)

    __setattr__ = __setitem__
    __getattr__ = dict.__getitem__

回答 22

@ derek73的答案非常简洁,但是不能被腌制或(深度)复制,并且返回None因缺少键。下面的代码解决了这个问题。

编辑:我没有看到上面的答案完全相同的点(已投票)。我将答案留在这里以供参考。

class dotdict(dict):
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

    def __getattr__(self, name):
        try:
            return self[name]
        except KeyError:
            raise AttributeError(name)

The answer of @derek73 is very neat, but it cannot be pickled nor (deep)copied, and it returns None for missing keys. The code below fixes this.

Edit: I did not see the answer above that addresses the exact same point (upvoted). I’m leaving the answer here for reference.

class dotdict(dict):
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

    def __getattr__(self, name):
        try:
            return self[name]
        except KeyError:
            raise AttributeError(name)

回答 23

一种精致的解决方案

class DotDict(dict):

    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

    def __getattr__(self, key):

        def typer(candidate):
            if isinstance(candidate, dict):
                return DotDict(candidate)

            if isinstance(candidate, str):  # iterable but no need to iter
                return candidate

            try:  # other iterable are processed as list
                return [typer(item) for item in candidate]
            except TypeError:
                return candidate

            return candidate

        return typer(dict.get(self, key))

A solution kind of delicate

class DotDict(dict):

    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

    def __getattr__(self, key):

        def typer(candidate):
            if isinstance(candidate, dict):
                return DotDict(candidate)

            if isinstance(candidate, str):  # iterable but no need to iter
                return candidate

            try:  # other iterable are processed as list
                return [typer(item) for item in candidate]
            except TypeError:
                return candidate

            return candidate

        return typer(dict.get(self, key))

python的for循环中的`continue`和`pass`之间有区别吗?

问题:python的for循环中的`continue`和`pass`之间有区别吗?

两个python关键字之间是否有任何显着差异continuepass如示例中所示

for element in some_list:
    if not element:
        pass

for element in some_list:
    if not element:
        continue

我应该知道吗?

Is there any significant difference between the two python keywords continue and pass like in the examples

for element in some_list:
    if not element:
        pass

and

for element in some_list:
    if not element:
        continue

I should be aware of?


回答 0

是的,他们做的事情完全不同。 pass只是不执行任何操作,而continue继续进行下一个循环迭代。在您的示例中,如果您在if:之后添加了另一个语句,则区别将变得显而易见pass。之后continue,就不会了。

>>> a = [0, 1, 2]
>>> for element in a:
...     if not element:
...         pass
...     print element
... 
0
1
2
>>> for element in a:
...     if not element:
...         continue
...     print element
... 
1
2

Yes, they do completely different things. pass simply does nothing, while continue goes on with the next loop iteration. In your example, the difference would become apparent if you added another statement after the if: After executing pass, this further statement would be executed. After continue, it wouldn’t.

>>> a = [0, 1, 2]
>>> for element in a:
...     if not element:
...         pass
...     print element
... 
0
1
2
>>> for element in a:
...     if not element:
...         continue
...     print element
... 
1
2

回答 1

是,有一点不同。continue强制循环从下一次迭代开始,而pass意味着“此处没有代码要执行”,并将继续执行其余部分或循环体。

运行这些,看看有什么区别:

for element in some_list:
    if not element:
        pass
    print 1 # will print after pass

for element in some_list:
   if not element:
       continue
   print 1 # will not print after continue

Yes, there is a difference. continue forces the loop to start at the next iteration while pass means “there is no code to execute here” and will continue through the remainder or the loop body.

Run these and see the difference:

for element in some_list:
    if not element:
        pass
    print 1 # will print after pass

for element in some_list:
   if not element:
       continue
   print 1 # will not print after continue

回答 2

continue 将跳回到循环的顶部。 pass将继续处理。

如果pass在循环的末尾,则差异可以忽略不计,因为流量始终会回到循环的顶部。

continue will jump back to the top of the loop. pass will continue processing.

if pass is at the end for the loop, the difference is negligible as the flow would just back to the top of the loop anyway.


回答 3

在您的示例中,这没有什么区别,因为两个语句都出现在循环的末尾。pass只是一个占位符,它什么都不做(它将执行传递给下一条语句)。continue另一方面,它有一个明确的目的:它告诉循环继续,就像刚刚重新启动一样。

for element in some_list:
    if not element:
        pass
    print element  

for element in some_list:
    if not element:
        continue
    print element

In your example, there will be no difference, since both statements appear at the end of the loop. pass is simply a placeholder, in that it does nothing (it passes execution to the next statement). continue, on the other hand, has a definite purpose: it tells the loop to continue as if it had just restarted.

for element in some_list:
    if not element:
        pass
    print element  

is very different from

for element in some_list:
    if not element:
        continue
    print element

回答 4

它们之间是有区别的,
continue跳过循环的当前迭代并执行下一个迭代。
pass什么也没做。这是一个空语句占位符。
我想给你一个例子,这将更好地阐明这一点。

>>> for element in some_list:
...     if element == 1:
...         print "Pass executed"
...         pass
...     print element
... 
0
Pass executed
1
2

>>> for element in some_list:
...     if element == 1:
...         print "Continue executed"
...         continue
...     print element
... 
0
Continue executed
2

There is a difference between them,
continue skips the loop’s current iteration and executes the next iteration.
pass does nothing. It’s an empty statement placeholder.
I would rather give you an example, which will clarify this more better.

>>> for element in some_list:
...     if element == 1:
...         print "Pass executed"
...         pass
...     print element
... 
0
Pass executed
1
2

>>> for element in some_list:
...     if element == 1:
...         print "Continue executed"
...         continue
...     print element
... 
0
Continue executed
2

回答 5

是,有一点不同。Continue实际上跳过了循环的当前迭代的其余部分(返回到开头)。Pass是不执行任何操作的空白语句。

查看python文档

Yes, there is a difference. Continue actually skips the rest of the current iteration of the loop (returning to the beginning). Pass is a blank statement that does nothing.

See the python docs


回答 6

在那些例子中,没有。如果该语句不是循环中的最后一条语句,那么它们将产生非常不同的效果。

In those examples, no. If the statement is not the very last in the loop then they have very different effects.


回答 7

在for循环中通过和继续之间的区别:

那么为什么要传入python呢?

如果要创建一个空的类,方法或块。

例子:

class MyException(Exception):
    pass


try:
   1/0
 except:
   pass

在上面的示例中如果没有“ pass”,将抛出IndentationError。

Difference between pass and continue in a for loop:

So why pass in python?

If you want to create a empty class, method or block.

Examples:

class MyException(Exception):
    pass


try:
   1/0
 except:
   pass

without ‘pass’ in the above examples will throw IndentationError.


回答 8

x = [1,2,3,4] 
for i in x:
    if i==2:
         pass  #Pass actually does nothing. It continues to execute statements below it.
         print "This statement is from pass."
for i in x:
    if i==2:
         continue #Continue gets back to top of the loop.And statements below continue are executed.
         print "This statement is from continue."

输出是

>>> This statement is from pass.

再次,让我们运行相同的代码进行较小的更改。

x = [1,2,3,4]
for i in x:
    if i==2:
       pass  #Pass actually does nothing. It continues to execute statements below it.
    print "This statement is from pass."
for i in x:
    if i==2:
        continue #Continue gets back to top of the loop.And statements below continue are executed.
    print "This statement is from continue."

输出是-

>>> This statement is from pass.
This statement is from pass.
This statement is from pass.
This statement is from pass.
This statement is from continue.
This statement is from continue.
This statement is from continue.

通过不执行任何操作。计算不受影响。但是继续返回到循环的顶部进行下一步计算。

x = [1,2,3,4] 
for i in x:
    if i==2:
         pass  #Pass actually does nothing. It continues to execute statements below it.
         print "This statement is from pass."
for i in x:
    if i==2:
         continue #Continue gets back to top of the loop.And statements below continue are executed.
         print "This statement is from continue."

The output is

>>> This statement is from pass.

Again, let run same code with minor changes.

x = [1,2,3,4]
for i in x:
    if i==2:
       pass  #Pass actually does nothing. It continues to execute statements below it.
    print "This statement is from pass."
for i in x:
    if i==2:
        continue #Continue gets back to top of the loop.And statements below continue are executed.
    print "This statement is from continue."

The output is –

>>> This statement is from pass.
This statement is from pass.
This statement is from pass.
This statement is from pass.
This statement is from continue.
This statement is from continue.
This statement is from continue.

Pass doesn’t do anything. Computation is unaffected. But continue gets back to top of the loop to procced with next computation.


回答 9

这样考虑:

通过: Python只能在缩进上工作!与其他语言不同,没有空的花括号。

因此,如果您不希望在条件为真的情况下不执行任何操作,则除了pass之外没有其他选择。

继续:仅在循环的情况下才有用。如果对于一定范围的值,您不想在该特定遍次的条件为真之后执行循环的其余语句,则必须使用continue。

Consider it this way:

Pass: Python works purely on indentation! There are no empty curly braces, unlike other languages.

So, if you want to do nothing in case a condition is true there is no option other than pass.

Continue: This is useful only in case of loops. In case, for a range of values, you don’t want to execute the remaining statements of the loop after that condition is true for that particular pass, then you will have to use continue.


回答 10

pass当您需要一些空函数,类或循环以用于将来的实现,而无需执行任何代码时,可以在场景中使用它。
continue用于在循环中未满足某些条件且没有条件且您需要跳过当前迭代并移至下一个迭代的场景中使用。

pass could be used in scenarios when you need some empty functions, classes or loops for future implementations, and there’s no requirement of executing any code.
continue is used in scenarios when no when some condition has met within a loop and you need to skip the current iteration and move to the next one.


下标序列时,Python中的::(双冒号)是什么?

问题:下标序列时,Python中的::(双冒号)是什么?

我知道我可以使用类似的string[3:4]方法在Python中获取子字符串,但是3表示somesequence[::3]什么呢?

I know that I can use something like string[3:4] to get a substring in Python, but what does the 3 mean in somesequence[::3]?


回答 0

它的意思是“第一个参数不加任何内容,第二个参数不加任何内容,然后跳三”。它获取序列中每隔三分之一的内容。 扩展片就是您想要的。Python 2.3的新功能

it means ‘nothing for the first argument, nothing for the second, and jump by three’. It gets every third item of the sequence sliced. Extended slices is what you want. New in Python 2.3


回答 1

可以将Python序列切片地址写为[start:end:step],并且可以删除任何start,stop或end。 a[::3]是序列的每三个元素。

Python sequence slice addresses can be written as a[start:end:step] and any of start, stop or end can be dropped. a[::3] is every third element of the sequence.


回答 2

seq[::n]是整个序列中每个n第-个项目的序列。

例:

>>> range(10)[::2]
[0, 2, 4, 6, 8]

语法为:

seq[start:end:step]

因此,您可以执行以下操作:

>>> range(100)[5:18:2]
[5, 7, 9, 11, 13, 15, 17]

seq[::n] is a sequence of each n-th item in the entire sequence.

Example:

>>> range(10)[::2]
[0, 2, 4, 6, 8]

The syntax is:

seq[start:end:step]

So you can do:

>>> range(100)[5:18:2]
[5, 7, 9, 11, 13, 15, 17]

回答 3

说明

s[i:j:k]是,根据文档 “从I S的切片与步骤k J”。当ij不存在时,将假定整个序列,因此s[::k]表示“第k个项目”。

例子

首先,让我们初始化一个列表:

>>> s = range(20)
>>> s
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]

让我们从中提取第三个项目s

>>> s[::3]
[0, 3, 6, 9, 12, 15, 18]

让我们从中提取第三个项目s[2:]

>>> s[2:]
[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>> s[2::3]
[2, 5, 8, 11, 14, 17]

让我们从中提取第三个项目s[5:12]

>>> s[5:12]
[5, 6, 7, 8, 9, 10, 11]
>>> s[5:12:3]
[5, 8, 11]

让我们从中提取第三个项目s[:10]

>>> s[:10]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> s[:10:3]
[0, 3, 6, 9]

Explanation

s[i:j:k] is, according to the documentation, “slice of s from i to j with step k”. When i and j are absent, the whole sequence is assumed and thus s[::k] means “every k-th item”.

Examples

First, let’s initialize a list:

>>> s = range(20)
>>> s
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]

Let’s take every 3rd item from s:

>>> s[::3]
[0, 3, 6, 9, 12, 15, 18]

Let’s take every 3rd item from s[2:]:

>>> s[2:]
[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>> s[2::3]
[2, 5, 8, 11, 14, 17]

Let’s take every 3rd item from s[5:12]:

>>> s[5:12]
[5, 6, 7, 8, 9, 10, 11]
>>> s[5:12:3]
[5, 8, 11]

Let’s take every 3rd item from s[:10]:

>>> s[:10]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> s[:10:3]
[0, 3, 6, 9]

回答 4

TL; DR

这个直观的示例将向您展示如何以一种非常有趣的方式(我保证)来巧妙地选择NumPy矩阵(二维数组)中的元素。下面的步骤2说明了该“双冒号”的用法::

(警告:这是一个特定于NumPy数组的示例,旨在说明“双冒号”用例::在多个轴上跳跃的用例。该示例未涵盖本地的Python数据结构,例如List)。

一个具体的例子来统治他们…

假设我们有一个如下的NumPy矩阵:

In [1]: import numpy as np

In [2]: X = np.arange(100).reshape(10,10)

In [3]: X
Out[3]:
array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
       [20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
       [30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
       [40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
       [50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
       [60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
       [70, 71, 72, 73, 74, 75, 76, 77, 78, 79],
       [80, 81, 82, 83, 84, 85, 86, 87, 88, 89],
       [90, 91, 92, 93, 94, 95, 96, 97, 98, 99]])

出于某种原因,您的老板希望您选择以下元素:

在此处输入图片说明

“但是怎么办?”……继续读下去!(我们可以分两步进行)

第1步-获取子集

在行方向和列方向上指定“开始索引”和“结束索引”。

在此处输入图片说明

在代码中:

In [5]: X2 = X[2:9,3:8]

In [6]: X2
Out[6]:
array([[23, 24, 25, 26, 27],
       [33, 34, 35, 36, 37],
       [43, 44, 45, 46, 47],
       [53, 54, 55, 56, 57],
       [63, 64, 65, 66, 67],
       [73, 74, 75, 76, 77],
       [83, 84, 85, 86, 87]])

注意,现在我们已经使用简单的开始和结束索引技术获得了子集。接下来,如何做“跳跃” …(继续阅读!)

第2步-选择元素(带有“跳转步骤”参数)

现在,我们可以在行方向和列方向上指定“跳转步骤”(以“跳转”方式选择元素),如下所示:

在此处输入图片说明

在代码中(请注意双冒号):

In [7]: X3 = X2[::3, ::2]

In [8]: X3
Out[8]:
array([[23, 25, 27],
       [53, 55, 57],
       [83, 85, 87]])

我们已经根据需要选择了所有元素!:)

 合并步骤1(开始和结束)和步骤2(“跳跃”)

现在我们知道了这个概念,我们可以轻松地将步骤1和步骤2合并为一个合并的步骤-为了紧凑:

In [9]: X4 = X[2:9,3:8][::3,::2]

    In [10]: X4
    Out[10]:
    array([[23, 25, 27],
           [53, 55, 57],
           [83, 85, 87]])

做完了!

TL;DR

This visual example will show you how to a neatly select elements in a NumPy Matrix (2 dimensional array) in a pretty entertaining way (I promise). Step 2 below illustrate the usage of that “double colons” :: in question.

(Caution: this is a NumPy array specific example with the aim of illustrating the a use case of “double colons” :: for jumping of elements in multiple axes. This example does not cover native Python data structures like List).

One concrete example to rule them all…

Say we have a NumPy matrix that looks like this:

In [1]: import numpy as np

In [2]: X = np.arange(100).reshape(10,10)

In [3]: X
Out[3]:
array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
       [20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
       [30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
       [40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
       [50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
       [60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
       [70, 71, 72, 73, 74, 75, 76, 77, 78, 79],
       [80, 81, 82, 83, 84, 85, 86, 87, 88, 89],
       [90, 91, 92, 93, 94, 95, 96, 97, 98, 99]])

Say for some reason, your boss wants you to select the following elements:

enter image description here

“But How???”… Read on! (We can do this in a 2-step approach)

Step 1 – Obtain subset

Specify the “start index” and “end index” in both row-wise and column-wise directions.

enter image description here

In code:

In [5]: X2 = X[2:9,3:8]

In [6]: X2
Out[6]:
array([[23, 24, 25, 26, 27],
       [33, 34, 35, 36, 37],
       [43, 44, 45, 46, 47],
       [53, 54, 55, 56, 57],
       [63, 64, 65, 66, 67],
       [73, 74, 75, 76, 77],
       [83, 84, 85, 86, 87]])

Notice now we’ve just obtained our subset, with the use of simple start and end indexing technique. Next up, how to do that “jumping”… (read on!)

Step 2 – Select elements (with the “jump step” argument)

We can now specify the “jump steps” in both row-wise and column-wise directions (to select elements in a “jumping” way) like this:

enter image description here

In code (note the double colons):

In [7]: X3 = X2[::3, ::2]

In [8]: X3
Out[8]:
array([[23, 25, 27],
       [53, 55, 57],
       [83, 85, 87]])

We have just selected all the elements as required! :)

 Consolidate Step 1 (start and end) and Step 2 (“jumping”)

Now we know the concept, we can easily combine step 1 and step 2 into one consolidated step – for compactness:

In [9]: X4 = X[2:9,3:8][::3,::2]

    In [10]: X4
    Out[10]:
    array([[23, 25, 27],
           [53, 55, 57],
           [83, 85, 87]])

Done!


回答 5

在Python中切片时,第三个参数是步骤。正如其他人提到的那样,请参阅扩展切片以获取良好的概述。

掌握了这些知识,[::3]就意味着您尚未为切片指定任何开始或结束索引。由于您已指定步骤,3因此将从something第一个索引开始每隔三分之一就输入一次。例如:

>>> '123123123'[::3]
'111'

When slicing in Python the third parameter is the step. As others mentioned, see Extended Slices for a nice overview.

With this knowledge, [::3] just means that you have not specified any start or end indices for your slice. Since you have specified a step, 3, this will take every third entry of something starting at the first index. For example:

>>> '123123123'[::3]
'111'

回答 6

您还可以在自己的自定义类中使用此表示法,使其随心所欲

class C(object):
    def __getitem__(self, k):
        return k

# Single argument is passed directly.
assert C()[0] == 0

# Multiple indices generate a tuple.
assert C()[0, 1] == (0, 1)

# Slice notation generates a slice object.
assert C()[1:2:3] == slice(1, 2, 3)

# If you omit any part of the slice notation, it becomes None.
assert C()[:] == slice(None, None, None)
assert C()[::] == slice(None, None, None)
assert C()[1::] == slice(1, None, None)
assert C()[:2:] == slice(None, 2, None)
assert C()[::3] == slice(None, None, 3)

# Tuple with a slice object:
assert C()[:, 1] == (slice(None, None, None), 1)

# Ellipsis class object.
assert C()[...] == Ellipsis

然后,我们可以按以下方式打开切片对象:

s = slice(1, 2, 3)
assert s.start == 1
assert s.stop == 2
assert s.step == 3

特别是在Numpy中,这用于在任何方向上切片多维数组。

当然,任何理智的API都应使用::3通常的“每3个”语义。

相关Ellipsis内容进一步涵盖:Ellipsis对象做什么?

You can also use this notation in your own custom classes to make it do whatever you want

class C(object):
    def __getitem__(self, k):
        return k

# Single argument is passed directly.
assert C()[0] == 0

# Multiple indices generate a tuple.
assert C()[0, 1] == (0, 1)

# Slice notation generates a slice object.
assert C()[1:2:3] == slice(1, 2, 3)

# If you omit any part of the slice notation, it becomes None.
assert C()[:] == slice(None, None, None)
assert C()[::] == slice(None, None, None)
assert C()[1::] == slice(1, None, None)
assert C()[:2:] == slice(None, 2, None)
assert C()[::3] == slice(None, None, 3)

# Tuple with a slice object:
assert C()[:, 1] == (slice(None, None, None), 1)

# Ellipsis class object.
assert C()[...] == Ellipsis

We can then open up slice objects as:

s = slice(1, 2, 3)
assert s.start == 1
assert s.stop == 2
assert s.step == 3

This is notably used in Numpy to slice multi-dimensional arrays in any direction.

Of course, any sane API should use ::3 with the usual “every 3” semantic.

The related Ellipsis is covered further at: What does the Ellipsis object do?


回答 7

第三个参数是步骤。因此[:: 3]将返回列表/字符串的每个第3个元素。

The third parameter is the step. So [::3] would return every 3rd element of the list/string.


回答 8

Python使用::分隔End,Start和Step值。

Python uses the :: to separate the End, the Start, and the Step value.


Python while语句的其他子句

问题:Python while语句的其他子句

我注意到以下代码在Python中是合法的。我的问题是为什么?是否有特定原因?

n = 5
while n != 0:
    print n
    n -= 1
else:
    print "what the..."

I’ve noticed the following code is legal in Python. My question is why? Is there a specific reason?

n = 5
while n != 0:
    print n
    n -= 1
else:
    print "what the..."

回答 0

else仅当您的while条件为假时才执行该子句。如果您break超出循环范围,或者引发了异常,则不会执行该异常。

考虑它的一种方法是关于条件的if / else构造:

if condition:
    handle_true()
else:
    handle_false()

与循环构造类似:

while condition:
    handle_true()
else:
    # condition is false now, handle and go on with the rest of the program
    handle_false()

一个示例可能类似于:

while value < threshold:
    if not process_acceptable_value(value):
        # something went wrong, exit the loop; don't pass go, don't collect 200
        break
    value = update(value)
else:
    # value >= threshold; pass go, collect 200
    handle_threshold_reached()

The else clause is only executed when your while condition becomes false. If you break out of the loop, or if an exception is raised, it won’t be executed.

One way to think about it is as an if/else construct with respect to the condition:

if condition:
    handle_true()
else:
    handle_false()

is analogous to the looping construct:

while condition:
    handle_true()
else:
    # condition is false now, handle and go on with the rest of the program
    handle_false()

An example might be along the lines of:

while value < threshold:
    if not process_acceptable_value(value):
        # something went wrong, exit the loop; don't pass go, don't collect 200
        break
    value = update(value)
else:
    # value >= threshold; pass go, collect 200
    handle_threshold_reached()

回答 1

else如果您通过退出循环条件或掉落try块的底部来正常退出一个块,则执行该子句。它执行,如果你break还是return一个块外,或引发异常。它不仅适用于while和for循环,还可以尝试块。

您通常会在通常会提前退出循环的地方找到它,而在循环结束时运行是意外/异常的情况。例如,如果要遍历列表以查找值:

for value in values:
    if value == 5:
        print "Found it!"
        break
else:
    print "Nowhere to be found. :-("

The else clause is executed if you exit a block normally, by hitting the loop condition or falling off the bottom of a try block. It is not executed if you break or return out of a block, or raise an exception. It works for not only while and for loops, but also try blocks.

You typically find it in places where normally you would exit a loop early, and running off the end of the loop is an unexpected/unusual occasion. For example, if you’re looping through a list looking for a value:

for value in values:
    if value == 5:
        print "Found it!"
        break
else:
    print "Nowhere to be found. :-("

回答 2

作为对的答复Is there a specific reason?,这是一个有趣的应用程序:突破了多个循环级别。

它是这样工作的:外循环在结尾处有一个中断,因此只能执行一次。但是,如果内部循环完成(未找到除数),那么它将到达else语句,并且永远不会到达外部中断。这样,内部循环中的中断将打破两个循环,而不仅仅是一个循环。

for k in [2, 3, 5, 7, 11, 13, 17, 25]:
    for m in range(2, 10):
        if k == m:
            continue
        print 'trying %s %% %s' % (k, m)
        if k % m == 0:
            print 'found a divisor: %d %% %d; breaking out of loop' % (k, m)
            break
    else:
        continue
    print 'breaking another level of loop'
    break
else:
    print 'no divisor could be found!'

对于whilefor循环else,除非break已使用,否则该语句将在最后执行。

在大多数情况下,有更好的方法可以做到这一点(将其包装到函数中或引发异常),但这是可行的!

In reply to Is there a specific reason?, here is one interesting application: breaking out of multiple levels of looping.

Here is how it works: the outer loop has a break at the end, so it would only be executed once. However, if the inner loop completes (finds no divisor), then it reaches the else statement and the outer break is never reached. This way, a break in the inner loop will break out of both loops, rather than just one.

for k in [2, 3, 5, 7, 11, 13, 17, 25]:
    for m in range(2, 10):
        if k == m:
            continue
        print 'trying %s %% %s' % (k, m)
        if k % m == 0:
            print 'found a divisor: %d %% %d; breaking out of loop' % (k, m)
            break
    else:
        continue
    print 'breaking another level of loop'
    break
else:
    print 'no divisor could be found!'

For both while and for loops, the else statement is executed at the end, unless break was used.

In most cases there are better ways to do this (wrapping it into a function or raising an exception), but this works!


回答 3

当while条件评估为false时,将执行else子句。

文档中

只要表达式为真,while语句将用于重复执行:

while_stmt ::=  "while" expression ":" suite
                ["else" ":" suite]

这将反复测试表达式,如果为true,则执行第一个套件;否则,将执行第一个套件。如果表达式为假(可能是第一次测试)else,则执行该子句的套件(如果存在),并终止循环。

break在第一个套件中执行的语句将终止循环,而不执行该else子句的套件。continue在第一个套件中执行的语句将跳过套件的其余部分,然后返回测试表达式。

The else-clause is executed when the while-condition evaluates to false.

From the documentation:

The while statement is used for repeated execution as long as an expression is true:

while_stmt ::=  "while" expression ":" suite
                ["else" ":" suite]

This repeatedly tests the expression and, if it is true, executes the first suite; if the expression is false (which may be the first time it is tested) the suite of the else clause, if present, is executed and the loop terminates.

A break statement executed in the first suite terminates the loop without executing the else clause’s suite. A continue statement executed in the first suite skips the rest of the suite and goes back to testing the expression.


回答 4

我的回答将集中于何时可以使用while / for-else。

乍一看,使用时似乎没有什么不同

while CONDITION:
    EXPRESSIONS
print 'ELSE'
print 'The next statement'

while CONDITION:
    EXPRESSIONS
else:
    print 'ELSE'
print 'The next statement'

因为该print 'ELSE'语句似乎总是在两种情况下都执行(无论是在while循环结束还是未运行时)。

然后,仅在print 'ELSE'不执行该语句时有所不同。这是break在下面的代码块中while

In [17]: i = 0

In [18]: while i < 5:
    print i
    if i == 2:
        break
    i = i +1
else:
    print 'ELSE'
print 'The next statement'
   ....:
0
1
2
The next statement

如果不同于:

In [19]: i = 0

In [20]: while i < 5:
    print i
    if i == 2:
        break
    i = i +1
print 'ELSE'
print 'The next statement'
   ....:
0
1
2
ELSE
The next statement

return 不在此类别中,因为它对以上两种情况具有相同的效果。

异常引发也不会引起差异,因为引发异常时,将在异常处理程序(块除外)中执行下一个代码的位置,将不会执行else子句中或while子句之后的代码。

My answer will focus on WHEN we can use while/for-else.

At the first glance, it seems there is no different when using

while CONDITION:
    EXPRESSIONS
print 'ELSE'
print 'The next statement'

and

while CONDITION:
    EXPRESSIONS
else:
    print 'ELSE'
print 'The next statement'

Because the print 'ELSE' statement seems always executed in both cases (both when the while loop finished or not run).

Then, it’s only different when the statement print 'ELSE' will not be executed. It’s when there is a breakinside the code block under while

In [17]: i = 0

In [18]: while i < 5:
    print i
    if i == 2:
        break
    i = i +1
else:
    print 'ELSE'
print 'The next statement'
   ....:
0
1
2
The next statement

If differ to:

In [19]: i = 0

In [20]: while i < 5:
    print i
    if i == 2:
        break
    i = i +1
print 'ELSE'
print 'The next statement'
   ....:
0
1
2
ELSE
The next statement

return is not in this category, because it does the same effect for two above cases.

exception raise also does not cause difference, because when it raises, where the next code will be executed is in exception handler (except block), the code in else clause or right after the while clause will not be executed.


回答 5

我知道这是个老问题,但是…

就像Raymond Hettinger所说的那样,应该调用它while/no_break而不是while/else
如果您查看此摘要,我会很容易理解不足。

n = 5
while n > 0:
    print n
    n -= 1
    if n == 2:
        break
if n == 0:
    print n

现在,我们无需在while循环后检查条件,而是可以将其交换else并摆脱该检查。

n = 5
while n > 0:
    print n
    n -= 1
    if n == 2:
        break
else:  # read it as "no_break"
    print n

我一直读它是while/no_break为了理解代码,而语法对我来说更有意义。

I know this is old question but…

As Raymond Hettinger said, it should be called while/no_break instead of while/else.
I find it easy to understeand if you look at this snippet.

n = 5
while n > 0:
    print n
    n -= 1
    if n == 2:
        break
if n == 0:
    print n

Now instead of checking condition after while loop we can swap it with else and get rid of that check.

n = 5
while n > 0:
    print n
    n -= 1
    if n == 2:
        break
else:  # read it as "no_break"
    print n

I always read it as while/no_break to understand the code and that syntax makes much more sense to me.


回答 6

else子句仅在while条件变为false 时执行。

这里有些例子:

示例1:最初条件为假,因此执行else子句

i = 99999999

while i < 5:
    print(i)
    i += 1
else:
    print('this')

输出:

this

例2:同时条件 i < 5从来没有成为因为假i == 3突破的循环,所以else从句未执行。

i = 0

while i < 5:
    print(i)
    if i == 3:
        break
    i += 1
else:
    print('this')

输出:

0
1
2
3

实施例3:而条件 i < 5成为当假i5,所以else从句被执行。

i = 0

while i < 5:
    print(i)
    i += 1
else:
    print('this')

输出:

0
1
2
3
4
this

The else clause is only executed when the while-condition becomes false.

Here are some examples:

Example 1: Initially the condition is false, so else-clause is executed.

i = 99999999

while i < 5:
    print(i)
    i += 1
else:
    print('this')

OUTPUT:

this

Example 2: The while-condition i < 5 never became false because i == 3 breaks the loop, so else-clause was not executed.

i = 0

while i < 5:
    print(i)
    if i == 3:
        break
    i += 1
else:
    print('this')

OUTPUT:

0
1
2
3

Example 3: The while-condition i < 5 became false when i was 5, so else-clause was executed.

i = 0

while i < 5:
    print(i)
    i += 1
else:
    print('this')

OUTPUT:

0
1
2
3
4
this

回答 7

else:仅当while循环不再满足其条件时才执行该语句(在您的示例中,当n != 0false时)。

所以输出是这样的:

5
4
3
2
1
what the...

The else: statement is executed when and only when the while loop no longer meets its condition (in your example, when n != 0 is false).

So the output would be this:

5
4
3
2
1
what the...

回答 8

如果while循环未中断,则执行Else。

我有点想用“跑步者”隐喻来思考它。

“其他”就像越过终点线,与您是从曲目的开头还是结尾开始无关。“其他人”只是,如果你在两者之间的某处破裂执行。

runner_at = 0 # or 10 makes no difference, if unlucky_sector is not 0-10
unlucky_sector = 6
while runner_at < 10:
    print("Runner at: ", runner_at)
    if runner_at == unlucky_sector:
        print("Runner fell and broke his foot. Will not reach finish.")
        break
    runner_at += 1
else:
    print("Runner has finished the race!") # Not executed if runner broke his foot.

主要用例是使用这种脱离嵌套循环的方式,或者仅在循环未在某个地方中断的情况下才想运行某些语句(认为中断是一种不寻常的情况)。

例如,以下是关于如何不使用变量或不尝试/不抓住而跳出内部循环的机制:

for i in [1,2,3]:
    for j in ['a', 'unlucky', 'c']:
        print(i, j)
        if j == 'unlucky':
            break
    else: 
        continue  # Only executed if inner loop didn't break.
    break         # This is only reached if inner loop 'breaked' out since continue didn't run. 

print("Finished")
# 1 a
# 1 b
# Finished

Else is executed if while loop did not break.

I kinda like to think of it with a ‘runner’ metaphor.

The “else” is like crossing the finish line, irrelevant of whether you started at the beginning or end of the track. “else” is only not executed if you break somewhere in between.

runner_at = 0 # or 10 makes no difference, if unlucky_sector is not 0-10
unlucky_sector = 6
while runner_at < 10:
    print("Runner at: ", runner_at)
    if runner_at == unlucky_sector:
        print("Runner fell and broke his foot. Will not reach finish.")
        break
    runner_at += 1
else:
    print("Runner has finished the race!") # Not executed if runner broke his foot.

Main use cases is using this breaking out of nested loops or if you want to run some statements only if loop didn’t break somewhere (think of breaking being an unusual situation).

For example, the following is a mechanism on how to break out of an inner loop without using variables or try/catch:

for i in [1,2,3]:
    for j in ['a', 'unlucky', 'c']:
        print(i, j)
        if j == 'unlucky':
            break
    else: 
        continue  # Only executed if inner loop didn't break.
    break         # This is only reached if inner loop 'breaked' out since continue didn't run. 

print("Finished")
# 1 a
# 1 b
# Finished

回答 9

在Python中更好地使用“ while:else:”构造应该是:如果在“ while”中没有执行循环,则执行“ else”语句。今天的工作方式没有意义,因为您可以使用下面的代码获得相同的结果…

n = 5
while n != 0:
    print n
    n -= 1
print "what the..."

The better use of ‘while: else:’ construction in Python should be if no loop is executed in ‘while’ then the ‘else’ statement is executed. The way it works today doesn’t make sense because you can use the code below with the same results…

n = 5
while n != 0:
    print n
    n -= 1
print "what the..."

回答 10

这对于社交互动很有用。

while (Date != "January 1st"):
    time.sleep(1)
else:
    print("Happy new year!")

It is useful for social interaction.

while (Date != "January 1st"):
    time.sleep(1)
else:
    print("Happy new year!")

访问像属性一样的字典键?

问题:访问像属性一样的字典键?

我发现访问dict键obj.foo而不是更为方便obj['foo'],因此我编写了以下代码段:

class AttributeDict(dict):
    def __getattr__(self, attr):
        return self[attr]
    def __setattr__(self, attr, value):
        self[attr] = value

但是,我认为一定有某些原因导致Python无法立即提供此功能。以这种方式访问​​字典键的注意事项和陷阱是什么?

I find it more convenient to access dict keys as obj.foo instead of obj['foo'], so I wrote this snippet:

class AttributeDict(dict):
    def __getattr__(self, attr):
        return self[attr]
    def __setattr__(self, attr, value):
        self[attr] = value

However, I assume that there must be some reason that Python doesn’t provide this functionality out of the box. What would be the caveats and pitfalls of accessing dict keys in this manner?


回答 0

最好的方法是:

class AttrDict(dict):
    def __init__(self, *args, **kwargs):
        super(AttrDict, self).__init__(*args, **kwargs)
        self.__dict__ = self

一些优点:

  • 它实际上有效!
  • 没有字典类方法被遮盖(例如,.keys()工作就很好。除非-当然-您为其分配了一些值,请参见下文)
  • 属性和项目始终保持同步
  • 尝试访问不存在的键作为属性正确引发,AttributeError而不是KeyError

缺点:

  • 如果这样的方法被传入的数据覆盖,它们.keys()无法正常工作
  • 在Python <2.7.4 / Python3 <3.2.3中导致内存泄漏
  • 皮林特(Pylint)E1123(unexpected-keyword-arg)E1103(maybe-no-member)
  • 对于初学者来说,这似乎是纯魔术。

简短说明

  • 所有python对象在内部将其属性存储在名为的字典中__dict__
  • 不需要内部字典__dict__必须是“仅是简单的字典”,因此我们可以将dict()内部字典的任何子类分配给它。
  • 在我们的例子中,我们只需分配要AttrDict()实例化的实例(就像在中一样__init__)。
  • 通过调用super()__init__()方法,我们可以确保它(已经)的行为与字典完全相同,因为该函数将调用所有字典实例化代码。

Python无法立即提供此功能的原因之一

如“ cons”列表中所述,这将存储键的命名空间(可能来自任意和/或不受信任的数据!)与内置dict方法属性的命名空间结合在一起。例如:

d = AttrDict()
d.update({'items':["jacket", "necktie", "trousers"]})
for k, v in d.items():    # TypeError: 'list' object is not callable
    print "Never reached!"

The best way to do this is:

class AttrDict(dict):
    def __init__(self, *args, **kwargs):
        super(AttrDict, self).__init__(*args, **kwargs)
        self.__dict__ = self

Some pros:

  • It actually works!
  • No dictionary class methods are shadowed (e.g. .keys() work just fine. Unless – of course – you assign some value to them, see below)
  • Attributes and items are always in sync
  • Trying to access non-existent key as an attribute correctly raises AttributeError instead of KeyError
  • Supports [Tab] autocompletion (e.g. in jupyter & ipython)

Cons:

  • Methods like .keys() will not work just fine if they get overwritten by incoming data
  • Each AttrDict instance actually stores 2 dictionaries, one inherited and another one in __dict__
  • Causes a memory leak in Python < 2.7.4 / Python3 < 3.2.3
  • Pylint goes bananas with E1123(unexpected-keyword-arg) and E1103(maybe-no-member)
  • For the uninitiated it seems like pure magic.

A short explanation on how this works

  • All python objects internally store their attributes in a dictionary that is named __dict__.
  • There is no requirement that the internal dictionary __dict__ would need to be “just a plain dict”, so we can assign any subclass of dict() to the internal dictionary.
  • In our case we simply assign the AttrDict() instance we are instantiating (as we are in __init__).
  • By calling super()‘s __init__() method we made sure that it (already) behaves exactly like a dictionary, since that function calls all the dictionary instantiation code.

One reason why Python doesn’t provide this functionality out of the box

As noted in the “cons” list, this combines the namespace of stored keys (which may come from arbitrary and/or untrusted data!) with the namespace of builtin dict method attributes. For example:

d = AttrDict()
d.update({'items':["jacket", "necktie", "trousers"]})
for k, v in d.items():    # TypeError: 'list' object is not callable
    print "Never reached!"

Update – 2020

Since this question was asked almost ten years ago, quite a bit has changed in Python itself since then.

While this approach is still valid for some cases, e.g. legacy projects stuck to older versions of Python and cases where you really need to handle dictionaries with very dynamic string keys – I think that in general the dataclasses introduced in Python 3.7 are the obvious/correct solution to vast majority of the use cases of AttrDict.


回答 1

如果使用数组表示法,则可以将所有合法字符串字符作为键的一部分。例如,obj['!#$%^&*()_']

You can have all legal string characters as part of the key if you use array notation. For example, obj['!#$%^&*()_']


回答 2

另一个SO问题中,有一个很好的实现示例,可以简化您的现有代码。怎么样:

class AttributeDict(dict): 
    __getattr__ = dict.__getitem__
    __setattr__ = dict.__setitem__

更简洁,不留任何余地额外的克鲁夫特进入你__getattr____setattr__功能的未来。

From This other SO question there’s a great implementation example that simplifies your existing code. How about:

class AttributeDict(dict):
    __slots__ = () 
    __getattr__ = dict.__getitem__
    __setattr__ = dict.__setitem__

Much more concise and doesn’t leave any room for extra cruft getting into your __getattr__ and __setattr__ functions in the future.


回答 3

我在哪里回答所问的问题

为什么Python不提供开箱即用的功能?

我怀疑这与PythonZen有关:“应该有一种-最好只有一种-显而易见的方法。” 这将创建两种显而易见的方式来访问字典中的值:obj['key']obj.key

注意事项和陷阱

这些可能包括代码不够清晰和混乱。也就是说,以下内容可能会使以后打算维护您代码的其他人感到困惑,甚至如果您暂时不使用它,也可能会使您感到困惑。再次,来自禅宗:“可读性很重要!”

>>> KEY = 'spam'
>>> d[KEY] = 1
>>> # Several lines of miscellaneous code here...
... assert d.spam == 1

如果d被实例化 KEY被定义或被 d[KEY]分配为远离d.spam使用的地方,则它很容易导致对正在执行的操作感到困惑,因为这不是常用的习惯用法。我知道这可能会使我感到困惑。

另外,如果您KEY按如下方式更改值(但未更改d.spam),则您将获得:

>>> KEY = 'foo'
>>> d[KEY] = 1
>>> # Several lines of miscellaneous code here...
... assert d.spam == 1
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
AttributeError: 'C' object has no attribute 'spam'

海事组织,不值得付出努力。

其他项目

正如其他人指出的那样,您可以使用任何可哈希对象(不仅仅是字符串)作为dict键。例如,

>>> d = {(2, 3): True,}
>>> assert d[(2, 3)] is True
>>> 

是合法的,但是

>>> C = type('C', (object,), {(2, 3): True})
>>> d = C()
>>> assert d.(2, 3) is True
  File "<stdin>", line 1
  d.(2, 3)
    ^
SyntaxError: invalid syntax
>>> getattr(d, (2, 3))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: getattr(): attribute name must be string
>>> 

不是。这使您可以访问字典键的所有可打印字符或其他可哈希对象的范围,而访问对象属性时则没有这些范围。这使诸如缓存对象元类之类的魔术成为可能,例如Python Cookbook(第9章)中的配方。

我在其中编辑

我更喜欢的美学spam.eggsspam['eggs'](我认为它看起来更清洁),我真的开始渴望这个功能时,我遇到了namedtuple。但是能够执行以下操作的便利性胜过它。

>>> KEYS = 'spam eggs ham'
>>> VALS = [1, 2, 3]
>>> d = {k: v for k, v in zip(KEYS.split(' '), VALS)}
>>> assert d == {'spam': 1, 'eggs': 2, 'ham': 3}
>>>

这是一个简单的示例,但是我经常发现自己在不同情况下使用dict而不是使用obj.key符号(即,当我需要从XML文件读取首选项时)。在其他情况下,出于美学原因,我倾向于实例化动态类并在其上添加一些属性,我将继续使用dict来保持一致性,以增强可读性。

我确信OP早就解决了这个问题,使他满意,但是如果他仍然想要此功能,那么我建议他从pypi下载提供该功能的软件包之一:

  • 是我更熟悉的一种。的子类dict,因此您具有所有功能。
  • AttrDict看起来也很不错,但是我并不熟悉它,也没有像 Bunch那样详细地浏览源代码。
  • Addict会得到积极维护,并提供类似attr的访问权限。
  • 如Rotareti的评论所述,Bunch已过时,但有一个名为Munch的活动叉子。

但是,为了提高代码的可读性,我强烈建议他不要混合使用自己的符号样式。如果他喜欢这种表示法,那么他应该简单地实例化一个动态对象,为其添加所需的属性,然后将其命名为day:

>>> C = type('C', (object,), {})
>>> d = C()
>>> d.spam = 1
>>> d.eggs = 2
>>> d.ham = 3
>>> assert d.__dict__ == {'spam': 1, 'eggs': 2, 'ham': 3}


我在其中更新,以在评论中回答后续问题

在下面的评论中,Elmo问:

如果您想更深入一点怎么办?(指type(…))

尽管我从未使用过这种用例(再次dict,为了保持一致性,我倾向于使用nested ),但是以下代码可以工作:

>>> C = type('C', (object,), {})
>>> d = C()
>>> for x in 'spam eggs ham'.split():
...     setattr(d, x, C())
...     i = 1
...     for y in 'one two three'.split():
...         setattr(getattr(d, x), y, i)
...         i += 1
...
>>> assert d.spam.__dict__ == {'one': 1, 'two': 2, 'three': 3}

Wherein I Answer the Question That Was Asked

Why doesn’t Python offer it out of the box?

I suspect that it has to do with the Zen of Python: “There should be one — and preferably only one — obvious way to do it.” This would create two obvious ways to access values from dictionaries: obj['key'] and obj.key.

Caveats and Pitfalls

These include possible lack of clarity and confusion in the code. i.e., the following could be confusing to someone else who is going in to maintain your code at a later date, or even to you, if you’re not going back into it for awhile. Again, from Zen: “Readability counts!”

>>> KEY = 'spam'
>>> d[KEY] = 1
>>> # Several lines of miscellaneous code here...
... assert d.spam == 1

If d is instantiated or KEY is defined or d[KEY] is assigned far away from where d.spam is being used, it can easily lead to confusion about what’s being done, since this isn’t a commonly-used idiom. I know it would have the potential to confuse me.

Additonally, if you change the value of KEY as follows (but miss changing d.spam), you now get:

>>> KEY = 'foo'
>>> d[KEY] = 1
>>> # Several lines of miscellaneous code here...
... assert d.spam == 1
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
AttributeError: 'C' object has no attribute 'spam'

IMO, not worth the effort.

Other Items

As others have noted, you can use any hashable object (not just a string) as a dict key. For example,

>>> d = {(2, 3): True,}
>>> assert d[(2, 3)] is True
>>> 

is legal, but

>>> C = type('C', (object,), {(2, 3): True})
>>> d = C()
>>> assert d.(2, 3) is True
  File "<stdin>", line 1
  d.(2, 3)
    ^
SyntaxError: invalid syntax
>>> getattr(d, (2, 3))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: getattr(): attribute name must be string
>>> 

is not. This gives you access to the entire range of printable characters or other hashable objects for your dictionary keys, which you do not have when accessing an object attribute. This makes possible such magic as a cached object metaclass, like the recipe from the Python Cookbook (Ch. 9).

Wherein I Editorialize

I prefer the aesthetics of spam.eggs over spam['eggs'] (I think it looks cleaner), and I really started craving this functionality when I met the namedtuple. But the convenience of being able to do the following trumps it.

>>> KEYS = 'spam eggs ham'
>>> VALS = [1, 2, 3]
>>> d = {k: v for k, v in zip(KEYS.split(' '), VALS)}
>>> assert d == {'spam': 1, 'eggs': 2, 'ham': 3}
>>>

This is a simple example, but I frequently find myself using dicts in different situations than I’d use obj.key notation (i.e., when I need to read prefs in from an XML file). In other cases, where I’m tempted to instantiate a dynamic class and slap some attributes on it for aesthetic reasons, I continue to use a dict for consistency in order to enhance readability.

I’m sure the OP has long-since resolved this to his satisfaction, but if he still wants this functionality, then I suggest he download one of the packages from pypi that provides it:

  • Bunch is the one I’m more familiar with. Subclass of dict, so you have all that functionality.
  • AttrDict also looks like it’s also pretty good, but I’m not as familiar with it and haven’t looked through the source in as much detail as I have Bunch.
  • Addict Is actively maintained and provides attr-like access and more.
  • As noted in the comments by Rotareti, Bunch has been deprecated, but there is an active fork called Munch.

However, in order to improve readability of his code I strongly recommend that he not mix his notation styles. If he prefers this notation then he should simply instantiate a dynamic object, add his desired attributes to it, and call it a day:

>>> C = type('C', (object,), {})
>>> d = C()
>>> d.spam = 1
>>> d.eggs = 2
>>> d.ham = 3
>>> assert d.__dict__ == {'spam': 1, 'eggs': 2, 'ham': 3}


Wherein I Update, to Answer a Follow-Up Question in the Comments

In the comments (below), Elmo asks:

What if you want to go one deeper? ( referring to type(…) )

While I’ve never used this use case (again, I tend to use nested dict, for consistency), the following code works:

>>> C = type('C', (object,), {})
>>> d = C()
>>> for x in 'spam eggs ham'.split():
...     setattr(d, x, C())
...     i = 1
...     for y in 'one two three'.split():
...         setattr(getattr(d, x), y, i)
...         i += 1
...
>>> assert d.spam.__dict__ == {'one': 1, 'two': 2, 'three': 3}

回答 4

注意:由于某些原因,像这样的类似乎破坏了多处理程序包。在找到该SO之前,我花了一段时间来解决这个错误: 在python多处理中查找异常

Caveat emptor: For some reasons classes like this seem to break the multiprocessing package. I just struggled with this bug for awhile before finding this SO: Finding exception in python multiprocessing


回答 5

您可以从标准库中提取一个方便的容器类:

from argparse import Namespace

避免必须复制代码位。没有标准的字典访问权限,但是如果您真的想要的话,很容易找回。argparse中的代码很简单,

class Namespace(_AttributeHolder):
    """Simple object for storing attributes.

    Implements equality by attribute names and values, and provides a simple
    string representation.
    """

    def __init__(self, **kwargs):
        for name in kwargs:
            setattr(self, name, kwargs[name])

    __hash__ = None

    def __eq__(self, other):
        return vars(self) == vars(other)

    def __ne__(self, other):
        return not (self == other)

    def __contains__(self, key):
        return key in self.__dict__

You can pull a convenient container class from the standard library:

from argparse import Namespace

to avoid having to copy around code bits. No standard dictionary access, but easy to get one back if you really want it. The code in argparse is simple,

class Namespace(_AttributeHolder):
    """Simple object for storing attributes.

    Implements equality by attribute names and values, and provides a simple
    string representation.
    """

    def __init__(self, **kwargs):
        for name in kwargs:
            setattr(self, name, kwargs[name])

    __hash__ = None

    def __eq__(self, other):
        return vars(self) == vars(other)

    def __ne__(self, other):
        return not (self == other)

    def __contains__(self, key):
        return key in self.__dict__

回答 6

如果您想要一个作为方法的键,例如__eq__或,该__getattr__怎么办?

而且,您将无法输入不以字母开头的条目,因此无法0343853用作键。

如果不想使用字符串怎么办?

What if you wanted a key which was a method, such as __eq__ or __getattr__?

And you wouldn’t be able to have an entry that didn’t start with a letter, so using 0343853 as a key is out.

And what if you didn’t want to use a string?


回答 7

元组可以使用dict键。您将如何访问构造中的元组?

同样,namedtuple是一种方便的结构,可以通过属性访问提供值。

tuples can be used dict keys. How would you access tuple in your construct?

Also, namedtuple is a convenient structure which can provide values via the attribute access.


回答 8

怎么样Prodict我写来统治它们的小Python类:)

另外,您将获得自动代码完成递归对象实例化自动类型转换

您可以完全按照要求进行:

p = Prodict()
p.foo = 1
p.bar = "baz"

示例1:类型提示

class Country(Prodict):
    name: str
    population: int

turkey = Country()
turkey.name = 'Turkey'
turkey.population = 79814871

自动编码完成

示例2:自动类型转换

germany = Country(name='Germany', population='82175700', flag_colors=['black', 'red', 'yellow'])

print(germany.population)  # 82175700
print(type(germany.population))  # <class 'int'>

print(germany.flag_colors)  # ['black', 'red', 'yellow']
print(type(germany.flag_colors))  # <class 'list'>

How about Prodict, the little Python class that I wrote to rule them all:)

Plus, you get auto code completion, recursive object instantiations and auto type conversion!

You can do exactly what you asked for:

p = Prodict()
p.foo = 1
p.bar = "baz"

Example 1: Type hinting

class Country(Prodict):
    name: str
    population: int

turkey = Country()
turkey.name = 'Turkey'
turkey.population = 79814871

auto code complete

Example 2: Auto type conversion

germany = Country(name='Germany', population='82175700', flag_colors=['black', 'red', 'yellow'])

print(germany.population)  # 82175700
print(type(germany.population))  # <class 'int'>

print(germany.flag_colors)  # ['black', 'red', 'yellow']
print(type(germany.flag_colors))  # <class 'list'>

回答 9

一般而言,它不起作用。并非所有有效的dict键都具有可寻址属性(“键”)。因此,您需要小心。

Python对象基本上都是字典。因此,我怀疑会有很多性能或其他损失。

It doesn’t work in generality. Not all valid dict keys make addressable attributes (“the key”). So, you’ll need to be careful.

Python objects are all basically dictionaries. So I doubt there is much performance or other penalty.


回答 10

这并没有解决最初的问题,但是对于像我这样在寻找提供此功能的库时到此结束的人很有用。

冰火它是为这个伟大的lib:https://github.com/mewwts/addict需要在前面的答案中提到的许多问题护理。

来自文档的示例:

body = {
    'query': {
        'filtered': {
            'query': {
                'match': {'description': 'addictive'}
            },
            'filter': {
                'term': {'created_by': 'Mats'}
            }
        }
    }
}

与瘾君子:

from addict import Dict
body = Dict()
body.query.filtered.query.match.description = 'addictive'
body.query.filtered.filter.term.created_by = 'Mats'

This doesn’t address the original question, but should be useful for people that, like me, end up here when looking for a lib that provides this functionality.

Addict it’s a great lib for this: https://github.com/mewwts/addict it takes care of many concerns mentioned in previous answers.

An example from the docs:

body = {
    'query': {
        'filtered': {
            'query': {
                'match': {'description': 'addictive'}
            },
            'filter': {
                'term': {'created_by': 'Mats'}
            }
        }
    }
}

With addict:

from addict import Dict
body = Dict()
body.query.filtered.query.match.description = 'addictive'
body.query.filtered.filter.term.created_by = 'Mats'

回答 11

我发现自己想知道python生态系统中“ dict keys as attr”的当前状态是什么。正如一些评论者所指出的那样,这可能不是您想要从头开始的事情,因为存在一些陷阱和脚枪,其中一些非常隐蔽。另外,我不建议将其Namespace用作基类,因为我一直走这条路,这并不漂亮。

幸运的是,有几个提供此功能的开源软件包,准备点安装!不幸的是,有几个软件包。简介,截至2019年12月。

竞争者(最近提交给master | #commits | #contribs | coverage%):

  • 瘾君子 (2019-04-28 | 217 | 22 | 100%)
  • 蒙克(2019年12月16日| 160 | 17 |?%)
  • easydict(2018-10-18 | 51 | 6 |?%)
  • attrdict(2019-02-01 | 108 | 5 | 100%)
  • prodict (2019年10月1日| 65 | 1 |?%)

不再维护或维护不足:

  • treedict(2014-03-28 | 95 | 2 |?%)
  • 一堆(2012-03-12 | 20 | 2 |?%)
  • NeoBunch

我目前建议吃午饭上瘾。他们拥有最多的提交,贡献者和发布,建议为每个构建一个健康的开源代码库。它们具有最干净的readme.md,100%的覆盖率和良好的测试集。

除了滚动我自己的dict / attr代码并浪费大量时间,因为我不知道所有这些选择之外,我在这场比赛中没有一只狗(到目前为止!)。将来我可能会为瘾君子/饥饿做贡献,因为我宁愿看到一个坚固的包装,也不愿看到一堆零散的包装。如果您喜欢它们,请贡献力量!特别是,看起来像munch可以使用codecov徽章,而上瘾者可以使用python版本的徽章。

瘾君子的优点:

  • 递归初始化(foo.abc =’bar’),类似dict的参数会上瘾。

瘾君子的缺点:

  • 阴影,typing.Dict如果你from addict import Dict
  • 没有密钥检查。由于允许递归初始化,因此如果您拼写错误的键,则只需创建一个新属性,而不是KeyError(感谢AljoSt)

嚼劲:

  • 独特的命名
  • JSON和YAML的内置ser / de函数

缺点:

  • 没有递归初始化/一次只能初始化一个attr

我在其中编辑

许多月前,当我使用文本编辑器在只有我自己或另一个开发人员的项目上编写python时,我喜欢dict-attrs的样式,即只需声明即可插入键foo.bar.spam = eggs。现在,我在团队中工作,并使用IDE进行所有操作,而我通常已经从这类数据结构和动态类型中移开了,而转向了静态分析,功能技术和类型提示。我已经开始尝试这种技术,并使用我自己设计的对象将Pstruct子类化:

class  BasePstruct(dict):
    def __getattr__(self, name):
        if name in self.__slots__:
            return self[name]
        return self.__getattribute__(name)

    def __setattr__(self, key, value):
        if key in self.__slots__:
            self[key] = value
            return
        if key in type(self).__dict__:
            self[key] = value
            return
        raise AttributeError(
            "type object '{}' has no attribute '{}'".format(type(self).__name__, key))


class FooPstruct(BasePstruct):
    __slots__ = ['foo', 'bar']

这为您提供了一个对象,其行为仍然像dict,但还使您可以更严格的方式访问诸如属性之类的键。这样做的好处是我(或代码的不幸使用者)确切知道哪些字段可以存在和不存在,并且IDE可以自动完成字段。子类化香草也dict意味着json序列化很容易。我认为这种想法的下一个发展将是一个自定义的protobuf生成器,它会发出这些接口,并且一个不错的替代方法是,您几乎可以免费通过gRPC获得跨语言的数据结构和IPC。

如果您决定采用attr-dict,则必须记录下期望的字段,以确保您自己(以及队友)的理智。

随时编辑/更新此帖子以保持最新!

I found myself wondering what the current state of “dict keys as attr” in the python ecosystem. As several commenters have pointed out, this is probably not something you want to roll your own from scratch, as there are several pitfalls and footguns, some of them very subtle. Also, I would not recommend using Namespace as a base class, I’ve been down that road, it isn’t pretty.

Fortunately, there are several open source packages providing this functionality, ready to pip install! Unfortunately, there are several packages. Here is a synopsis, as of Dec 2019.

Contenders (most recent commit to master|#commits|#contribs|coverage%):

  • addict (2019-04-28 | 217 | 22 | 100%)
  • munch (2019-12-16 | 160 | 17 | ?%)
  • easydict (2018-10-18 | 51 | 6 | ?%)
  • attrdict (2019-02-01 | 108 | 5 | 100%)
  • prodict (2019-10-01 | 65 | 1 | ?%)

No longer maintained or under-maintained:

  • treedict (2014-03-28 | 95 | 2 | ?%)
  • bunch (2012-03-12 | 20 | 2 | ?%)
  • NeoBunch

I currently recommend munch or addict. They have the most commits, contributors, and releases, suggesting a healthy open-source codebase for each. They have the cleanest-looking readme.md, 100% coverage, and good looking set of tests.

I do not have a dog in this race (for now!), besides having rolled my own dict/attr code and wasted a ton of time because I was not aware of all these options :). I may contribute to addict/munch in the future as I would rather see one solid package than a bunch of fragmented ones. If you like them, contribute! In particular, looks like munch could use a codecov badge and addict could use a python version badge.

addict pros:

  • recursive initialization (foo.a.b.c = ‘bar’), dict-like arguments become addict.Dict

addict cons:

  • shadows typing.Dict if you from addict import Dict
  • No key checking. Due to allowing recursive init, if you misspell a key, you just create a new attribute, rather than KeyError (thanks AljoSt)

munch pros:

  • unique naming
  • built-in ser/de functions for JSON and YAML

munch cons:

  • no recursive init / only can init one attr at a time

Wherein I Editorialize

Many moons ago, when I used text editors to write python, on projects with only myself or one other dev, I liked the style of dict-attrs, the ability to insert keys by just declaring foo.bar.spam = eggs. Now I work on teams, and use an IDE for everything, and I have drifted away from these sorts of data structures and dynamic typing in general, in favor of static analysis, functional techniques and type hints. I’ve started experimenting with this technique, subclassing Pstruct with objects of my own design:

class  BasePstruct(dict):
    def __getattr__(self, name):
        if name in self.__slots__:
            return self[name]
        return self.__getattribute__(name)

    def __setattr__(self, key, value):
        if key in self.__slots__:
            self[key] = value
            return
        if key in type(self).__dict__:
            self[key] = value
            return
        raise AttributeError(
            "type object '{}' has no attribute '{}'".format(type(self).__name__, key))


class FooPstruct(BasePstruct):
    __slots__ = ['foo', 'bar']

This gives you an object which still behaves like a dict, but also lets you access keys like attributes, in a much more rigid fashion. The advantage here is I (or the hapless consumers of your code) know exactly what fields can and can’t exist, and the IDE can autocomplete fields. Also subclassing vanilla dict means json serialization is easy. I think the next evolution in this idea would be a custom protobuf generator which emits these interfaces, and a nice knock-on is you get cross-language data structures and IPC via gRPC for nearly free.

If you do decide to go with attr-dicts, it’s essential to document what fields are expected, for your own (and your teammates’) sanity.

Feel free to edit/update this post to keep it recent!


回答 12

这是使用内置的不可变记录的简短示例collections.namedtuple

def record(name, d):
    return namedtuple(name, d.keys())(**d)

和用法示例:

rec = record('Model', {
    'train_op': train_op,
    'loss': loss,
})

print rec.loss(..)

Here’s a short example of immutable records using built-in collections.namedtuple:

def record(name, d):
    return namedtuple(name, d.keys())(**d)

and a usage example:

rec = record('Model', {
    'train_op': train_op,
    'loss': loss,
})

print rec.loss(..)

回答 13

只是为了增加答案的多样性,sci-kit learning已将其实现为Bunch

class Bunch(dict):                                                              
    """ Scikit Learn's container object                                         

    Dictionary-like object that exposes its keys as attributes.                 
    >>> b = Bunch(a=1, b=2)                                                     
    >>> b['b']                                                                  
    2                                                                           
    >>> b.b                                                                     
    2                                                                           
    >>> b.c = 6                                                                 
    >>> b['c']                                                                  
    6                                                                           
    """                                                                         

    def __init__(self, **kwargs):                                               
        super(Bunch, self).__init__(kwargs)                                     

    def __setattr__(self, key, value):                                          
        self[key] = value                                                       

    def __dir__(self):                                                          
        return self.keys()                                                      

    def __getattr__(self, key):                                                 
        try:                                                                    
            return self[key]                                                    
        except KeyError:                                                        
            raise AttributeError(key)                                           

    def __setstate__(self, state):                                              
        pass                       

您所需要做的就是获取setattrgetattr方法- getattr检查dict键,然后继续检查实际属性。这setstaet是用于酸洗/解开“束”的修复程序-如果您感兴趣,请查看https://github.com/scikit-learn/scikit-learn/issues/6196

Just to add some variety to the answer, sci-kit learn has this implemented as a Bunch:

class Bunch(dict):                                                              
    """ Scikit Learn's container object                                         

    Dictionary-like object that exposes its keys as attributes.                 
    >>> b = Bunch(a=1, b=2)                                                     
    >>> b['b']                                                                  
    2                                                                           
    >>> b.b                                                                     
    2                                                                           
    >>> b.c = 6                                                                 
    >>> b['c']                                                                  
    6                                                                           
    """                                                                         

    def __init__(self, **kwargs):                                               
        super(Bunch, self).__init__(kwargs)                                     

    def __setattr__(self, key, value):                                          
        self[key] = value                                                       

    def __dir__(self):                                                          
        return self.keys()                                                      

    def __getattr__(self, key):                                                 
        try:                                                                    
            return self[key]                                                    
        except KeyError:                                                        
            raise AttributeError(key)                                           

    def __setstate__(self, state):                                              
        pass                       

All you need is to get the setattr and getattr methods – the getattr checks for dict keys and the moves on to checking for actual attributes. The setstaet is a fix for fix for pickling/unpickling “bunches” – if inerested check https://github.com/scikit-learn/scikit-learn/issues/6196


回答 14

无需编写自己的 setattr()和getattr()即可。

类对象的优势可能在类定义和继承中发挥了作用。

No need to write your own as setattr() and getattr() already exist.

The advantage of class objects probably comes into play in class definition and inheritance.


回答 15

我是根据该线程的输入创建的。不过,我需要使用odict,因此必须重写get和set attr。我认为这应适用于大多数特殊用途。

用法如下所示:

# Create an ordered dict normally...
>>> od = OrderedAttrDict()
>>> od["a"] = 1
>>> od["b"] = 2
>>> od
OrderedAttrDict([('a', 1), ('b', 2)])

# Get and set data using attribute access...
>>> od.a
1
>>> od.b = 20
>>> od
OrderedAttrDict([('a', 1), ('b', 20)])

# Setting a NEW attribute only creates it on the instance, not the dict...
>>> od.c = 8
>>> od
OrderedAttrDict([('a', 1), ('b', 20)])
>>> od.c
8

Class:

class OrderedAttrDict(odict.OrderedDict):
    """
    Constructs an odict.OrderedDict with attribute access to data.

    Setting a NEW attribute only creates it on the instance, not the dict.
    Setting an attribute that is a key in the data will set the dict data but 
    will not create a new instance attribute
    """
    def __getattr__(self, attr):
        """
        Try to get the data. If attr is not a key, fall-back and get the attr
        """
        if self.has_key(attr):
            return super(OrderedAttrDict, self).__getitem__(attr)
        else:
            return super(OrderedAttrDict, self).__getattr__(attr)


    def __setattr__(self, attr, value):
        """
        Try to set the data. If attr is not a key, fall-back and set the attr
        """
        if self.has_key(attr):
            super(OrderedAttrDict, self).__setitem__(attr, value)
        else:
            super(OrderedAttrDict, self).__setattr__(attr, value)

这是线程中已经提到的非常酷的模式,但是如果您只想接受一个dict并将其转换为可以在IDE中自动完成的对象,等等:

class ObjectFromDict(object):
    def __init__(self, d):
        self.__dict__ = d

I created this based on the input from this thread. I need to use odict though, so I had to override get and set attr. I think this should work for the majority of special uses.

Usage looks like this:

# Create an ordered dict normally...
>>> od = OrderedAttrDict()
>>> od["a"] = 1
>>> od["b"] = 2
>>> od
OrderedAttrDict([('a', 1), ('b', 2)])

# Get and set data using attribute access...
>>> od.a
1
>>> od.b = 20
>>> od
OrderedAttrDict([('a', 1), ('b', 20)])

# Setting a NEW attribute only creates it on the instance, not the dict...
>>> od.c = 8
>>> od
OrderedAttrDict([('a', 1), ('b', 20)])
>>> od.c
8

The class:

class OrderedAttrDict(odict.OrderedDict):
    """
    Constructs an odict.OrderedDict with attribute access to data.

    Setting a NEW attribute only creates it on the instance, not the dict.
    Setting an attribute that is a key in the data will set the dict data but 
    will not create a new instance attribute
    """
    def __getattr__(self, attr):
        """
        Try to get the data. If attr is not a key, fall-back and get the attr
        """
        if self.has_key(attr):
            return super(OrderedAttrDict, self).__getitem__(attr)
        else:
            return super(OrderedAttrDict, self).__getattr__(attr)


    def __setattr__(self, attr, value):
        """
        Try to set the data. If attr is not a key, fall-back and set the attr
        """
        if self.has_key(attr):
            super(OrderedAttrDict, self).__setitem__(attr, value)
        else:
            super(OrderedAttrDict, self).__setattr__(attr, value)

This is a pretty cool pattern already mentioned in the thread, but if you just want to take a dict and convert it to an object that works with auto-complete in an IDE, etc:

class ObjectFromDict(object):
    def __init__(self, d):
        self.__dict__ = d

回答 16

显然,现在有这个图书馆- https://pypi.python.org/pypi/attrdict -它实现了这个确切的功能加上递归合并和JSON负载。可能值得一看。

Apparently there is now a library for this – https://pypi.python.org/pypi/attrdict – which implements this exact functionality plus recursive merging and json loading. Might be worth a look.


回答 17

这就是我用的

args = {
        'batch_size': 32,
        'workers': 4,
        'train_dir': 'train',
        'val_dir': 'val',
        'lr': 1e-3,
        'momentum': 0.9,
        'weight_decay': 1e-4
    }
args = namedtuple('Args', ' '.join(list(args.keys())))(**args)

print (args.lr)

This is what I use

args = {
        'batch_size': 32,
        'workers': 4,
        'train_dir': 'train',
        'val_dir': 'val',
        'lr': 1e-3,
        'momentum': 0.9,
        'weight_decay': 1e-4
    }
args = namedtuple('Args', ' '.join(list(args.keys())))(**args)

print (args.lr)

回答 18

您可以使用我刚刚制作的此类来做。通过此类,您可以Map像其他字典(包括json序列化)一样使用该对象,也可以使用点符号。希望对您有帮助:

class Map(dict):
    """
    Example:
    m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
    """
    def __init__(self, *args, **kwargs):
        super(Map, self).__init__(*args, **kwargs)
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.iteritems():
                    self[k] = v

        if kwargs:
            for k, v in kwargs.iteritems():
                self[k] = v

    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(Map, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(Map, self).__delitem__(key)
        del self.__dict__[key]

用法示例:

m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
# Add new key
m.new_key = 'Hello world!'
print m.new_key
print m['new_key']
# Update values
m.new_key = 'Yay!'
# Or
m['new_key'] = 'Yay!'
# Delete key
del m.new_key
# Or
del m['new_key']

You can do it using this class I just made. With this class you can use the Map object like another dictionary(including json serialization) or with the dot notation. I hope help you:

class Map(dict):
    """
    Example:
    m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
    """
    def __init__(self, *args, **kwargs):
        super(Map, self).__init__(*args, **kwargs)
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.iteritems():
                    self[k] = v

        if kwargs:
            for k, v in kwargs.iteritems():
                self[k] = v

    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(Map, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(Map, self).__delitem__(key)
        del self.__dict__[key]

Usage examples:

m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
# Add new key
m.new_key = 'Hello world!'
print m.new_key
print m['new_key']
# Update values
m.new_key = 'Yay!'
# Or
m['new_key'] = 'Yay!'
# Delete key
del m.new_key
# Or
del m['new_key']

回答 19

让我发布另一个实现,该实现基于Kinvais的答案,但整合了http://databio.org/posts/python_AttributeDict.html中提出的AttributeDict的思想。

这个版本的优点是它也适用于嵌套字典:

class AttrDict(dict):
    """
    A class to convert a nested Dictionary into an object with key-values
    that are accessible using attribute notation (AttrDict.attribute) instead of
    key notation (Dict["key"]). This class recursively sets Dicts to objects,
    allowing you to recurse down nested dicts (like: AttrDict.attr.attr)
    """

    # Inspired by:
    # http://stackoverflow.com/a/14620633/1551810
    # http://databio.org/posts/python_AttributeDict.html

    def __init__(self, iterable, **kwargs):
        super(AttrDict, self).__init__(iterable, **kwargs)
        for key, value in iterable.items():
            if isinstance(value, dict):
                self.__dict__[key] = AttrDict(value)
            else:
                self.__dict__[key] = value

Let me post another implementation, which builds upon the answer of Kinvais, but integrates ideas from the AttributeDict proposed in http://databio.org/posts/python_AttributeDict.html.

The advantage of this version is that it also works for nested dictionaries:

class AttrDict(dict):
    """
    A class to convert a nested Dictionary into an object with key-values
    that are accessible using attribute notation (AttrDict.attribute) instead of
    key notation (Dict["key"]). This class recursively sets Dicts to objects,
    allowing you to recurse down nested dicts (like: AttrDict.attr.attr)
    """

    # Inspired by:
    # http://stackoverflow.com/a/14620633/1551810
    # http://databio.org/posts/python_AttributeDict.html

    def __init__(self, iterable, **kwargs):
        super(AttrDict, self).__init__(iterable, **kwargs)
        for key, value in iterable.items():
            if isinstance(value, dict):
                self.__dict__[key] = AttrDict(value)
            else:
                self.__dict__[key] = value

回答 20

class AttrDict(dict):

     def __init__(self):
           self.__dict__ = self

if __name__ == '____main__':

     d = AttrDict()
     d['ray'] = 'hope'
     d.sun = 'shine'  >>> Now we can use this . notation
     print d['ray']
     print d.sun
class AttrDict(dict):

     def __init__(self):
           self.__dict__ = self

if __name__ == '____main__':

     d = AttrDict()
     d['ray'] = 'hope'
     d.sun = 'shine'  >>> Now we can use this . notation
     print d['ray']
     print d.sun

回答 21

解决方法是:

DICT_RESERVED_KEYS = vars(dict).keys()


class SmartDict(dict):
    """
    A Dict which is accessible via attribute dot notation
    """
    def __init__(self, *args, **kwargs):
        """
        :param args: multiple dicts ({}, {}, ..)
        :param kwargs: arbitrary keys='value'

        If ``keyerror=False`` is passed then not found attributes will
        always return None.
        """
        super(SmartDict, self).__init__()
        self['__keyerror'] = kwargs.pop('keyerror', True)
        [self.update(arg) for arg in args if isinstance(arg, dict)]
        self.update(kwargs)

    def __getattr__(self, attr):
        if attr not in DICT_RESERVED_KEYS:
            if self['__keyerror']:
                return self[attr]
            else:
                return self.get(attr)
        return getattr(self, attr)

    def __setattr__(self, key, value):
        if key in DICT_RESERVED_KEYS:
            raise AttributeError("You cannot set a reserved name as attribute")
        self.__setitem__(key, value)

    def __copy__(self):
        return self.__class__(self)

    def copy(self):
        return self.__copy__()

Solution is:

DICT_RESERVED_KEYS = vars(dict).keys()


class SmartDict(dict):
    """
    A Dict which is accessible via attribute dot notation
    """
    def __init__(self, *args, **kwargs):
        """
        :param args: multiple dicts ({}, {}, ..)
        :param kwargs: arbitrary keys='value'

        If ``keyerror=False`` is passed then not found attributes will
        always return None.
        """
        super(SmartDict, self).__init__()
        self['__keyerror'] = kwargs.pop('keyerror', True)
        [self.update(arg) for arg in args if isinstance(arg, dict)]
        self.update(kwargs)

    def __getattr__(self, attr):
        if attr not in DICT_RESERVED_KEYS:
            if self['__keyerror']:
                return self[attr]
            else:
                return self.get(attr)
        return getattr(self, attr)

    def __setattr__(self, key, value):
        if key in DICT_RESERVED_KEYS:
            raise AttributeError("You cannot set a reserved name as attribute")
        self.__setitem__(key, value)

    def __copy__(self):
        return self.__class__(self)

    def copy(self):
        return self.__copy__()

回答 22

以这种方式访问​​字典键的注意事项和陷阱是什么?

正如@Henry所建议的那样,可能无法在dict中使用点分访问的一个原因是它将dict关键字名限制为python有效变量,从而限制了所有可能的名称。

下面的示例说明了在给定命令的情况下,为什么点访问通常不会有用d

有效期

以下属性在Python中无效:

d.1_foo                           # enumerated names
d./bar                            # path names
d.21.7, d.12:30                   # decimals, time
d.""                              # empty strings
d.john doe, d.denny's             # spaces, misc punctuation 
d.3 * x                           # expressions  

样式

PEP8约定将对属性命名施加软约束:

A.保留关键字(或内置函数)名称:

d.in
d.False, d.True
d.max, d.min
d.sum
d.id

如果函数参数的名称与保留关键字冲突,通常最好在其后附加一个下划线…

B.关于方法变量名的大小写规则:

变量名遵循与函数名相同的约定。

d.Firstname
d.Country

使用函数命名规则:小写字母,单词以下划线分隔,以提高可读性。


有时候,像熊猫这样的图书馆会引起这些担忧,该允许按名称对DataFrame列进行点访问。解决命名限制的默认机制也是数组符号-方括号中的字符串。

如果这些约束不适用于您的用例,则点访问数据结构上有多个选项。

What would be the caveats and pitfalls of accessing dict keys in this manner?

As @Henry suggests, one reason dotted-access may not be used in dicts is that it limits dict key names to python-valid variables, thereby restricting all possible names.

The following are examples on why dotted-access would not be helpful in general, given a dict, d:

Validity

The following attributes would be invalid in Python:

d.1_foo                           # enumerated names
d./bar                            # path names
d.21.7, d.12:30                   # decimals, time
d.""                              # empty strings
d.john doe, d.denny's             # spaces, misc punctuation 
d.3 * x                           # expressions  

Style

PEP8 conventions would impose a soft constraint on attribute naming:

A. Reserved keyword (or builtin function) names:

d.in
d.False, d.True
d.max, d.min
d.sum
d.id

If a function argument’s name clashes with a reserved keyword, it is generally better to append a single trailing underscore …

B. The case rule on methods and variable names:

Variable names follow the same convention as function names.

d.Firstname
d.Country

Use the function naming rules: lowercase with words separated by underscores as necessary to improve readability.


Sometimes these concerns are raised in libraries like pandas, which permits dotted-access of DataFrame columns by name. The default mechanism to resolve naming restrictions is also array-notation – a string within brackets.

If these constraints do not apply to your use case, there are several options on dotted-access data structures.


回答 23

您可以使用dict_to_obj https://pypi.org/project/dict-to-obj/ 它确实满足您的要求

From dict_to_obj import DictToObj
a = {
'foo': True
}
b = DictToObj(a)
b.foo
True

You can use dict_to_obj https://pypi.org/project/dict-to-obj/ It does exactly what you asked for

From dict_to_obj import DictToObj
a = {
'foo': True
}
b = DictToObj(a)
b.foo
True


回答 24

这不是一个“好”答案,但我认为这很不错(它不能处理当前形式的嵌套字典。)只需将您的字典包装在一个函数中:

def make_funcdict(d=None, **kwargs)
    def funcdict(d=None, **kwargs):
        if d is not None:
            funcdict.__dict__.update(d)
        funcdict.__dict__.update(kwargs)
        return funcdict.__dict__
    funcdict(d, **kwargs)
    return funcdict

现在您的语法略有不同。要像属性一样访问字典项f.key。要以通常的方式访问dict项目(和其他dict方法)f()['key'],我们可以方便地通过使用关键字参数和/或字典调用f来更新dict

d = {'name':'Henry', 'age':31}
d = make_funcdict(d)
>>> for key in d():
...     print key
... 
age
name
>>> print d.name
... Henry
>>> print d.age
... 31
>>> d({'Height':'5-11'}, Job='Carpenter')
... {'age': 31, 'name': 'Henry', 'Job': 'Carpenter', 'Height': '5-11'}

在那里。如果有人建议使用此方法的优点和缺点,我会很高兴。

This isn’t a ‘good’ answer, but I thought this was nifty (it doesn’t handle nested dicts in current form). Simply wrap your dict in a function:

def make_funcdict(d=None, **kwargs)
    def funcdict(d=None, **kwargs):
        if d is not None:
            funcdict.__dict__.update(d)
        funcdict.__dict__.update(kwargs)
        return funcdict.__dict__
    funcdict(d, **kwargs)
    return funcdict

Now you have slightly different syntax. To acces the dict items as attributes do f.key. To access the dict items (and other dict methods) in the usual manner do f()['key'] and we can conveniently update the dict by calling f with keyword arguments and/or a dictionary

Example

d = {'name':'Henry', 'age':31}
d = make_funcdict(d)
>>> for key in d():
...     print key
... 
age
name
>>> print d.name
... Henry
>>> print d.age
... 31
>>> d({'Height':'5-11'}, Job='Carpenter')
... {'age': 31, 'name': 'Henry', 'Job': 'Carpenter', 'Height': '5-11'}

And there it is. I’ll be happy if anyone suggests benefits and drawbacks of this method.


回答 25

正如Doug指出的那样,有一个Bunch软件包可用于实现该obj.key功能。其实有一个较新的版本叫做

NeoBunch

它具有很大的功能,可以通过它的neobunchify函数将您的字典转换为NeoBunch对象。我经常使用Mako模板,并且由于NeoBunch对象传递数据使它们更具可读性,因此,如果您碰巧最终在Python程序中使用了普通字典,但是想要在Mako模板中使用点符号,则可以这样使用:

from mako.template import Template
from neobunch import neobunchify

mako_template = Template(filename='mako.tmpl', strict_undefined=True)
data = {'tmpl_data': [{'key1': 'value1', 'key2': 'value2'}]}
with open('out.txt', 'w') as out_file:
    out_file.write(mako_template.render(**neobunchify(data)))

Mako模板可能如下所示:

% for d in tmpl_data:
Column1     Column2
${d.key1}   ${d.key2}
% endfor

As noted by Doug there’s a Bunch package which you can use to achieve the obj.key functionality. Actually there’s a newer version called

NeoBunch

It has though a great feature converting your dict to a NeoBunch object through its neobunchify function. I use Mako templates a lot and passing data as NeoBunch objects makes them far more readable, so if you happen to end up using a normal dict in your Python program but want the dot notation in a Mako template you can use it that way:

from mako.template import Template
from neobunch import neobunchify

mako_template = Template(filename='mako.tmpl', strict_undefined=True)
data = {'tmpl_data': [{'key1': 'value1', 'key2': 'value2'}]}
with open('out.txt', 'w') as out_file:
    out_file.write(mako_template.render(**neobunchify(data)))

And the Mako template could look like:

% for d in tmpl_data:
Column1     Column2
${d.key1}   ${d.key2}
% endfor

回答 26

最简单的方法是定义一个类,我们将其称为命名空间。它在字典上使用对象dict .update()。然后,该字典将被视为对象。

class Namespace(object):
    '''
    helps referencing object in a dictionary as dict.key instead of dict['key']
    '''
    def __init__(self, adict):
        self.__dict__.update(adict)



Person = Namespace({'name': 'ahmed',
                     'age': 30}) #--> added for edge_cls


print(Person.name)

The easiest way is to define a class let’s call it Namespace. which uses the object dict.update() on the dict. Then, the dict will be treated as an object.

class Namespace(object):
    '''
    helps referencing object in a dictionary as dict.key instead of dict['key']
    '''
    def __init__(self, adict):
        self.__dict__.update(adict)



Person = Namespace({'name': 'ahmed',
                     'age': 30}) #--> added for edge_cls


print(Person.name)

您如何用Python表达二进制文字?

问题:您如何用Python表达二进制文字?

如何使用Python文字将整数表示为二进制数?

我很容易找到十六进制的答案:

>>> 0x12AF
4783
>>> 0x100
256

和八进制:

>>> 01267
695
>>> 0100
64

您如何使用文字在Python中表示二进制?


答案摘要

  • Python 2.5及更早版本:可以使用,int('01010101111',2)但不能使用文字来表示二进制。
  • Python 2.5和更早版本:无法表达二进制文字。
  • Python 2.6 beta:您可以这样做:0b11001110B1100111
  • Python 2.6 beta:还将允许0o270O27(第二个字符是字母O)表示一个八进制。
  • Python 3.0 beta:与2.6相同,但将不再允许使用较旧027的八进制语法。

How do you express an integer as a binary number with Python literals?

I was easily able to find the answer for hex:

>>> 0x12AF
4783
>>> 0x100
256

and octal:

>>> 01267
695
>>> 0100
64

How do you use literals to express binary in Python?


Summary of Answers

  • Python 2.5 and earlier: can express binary using int('01010101111',2) but not with a literal.
  • Python 2.5 and earlier: there is no way to express binary literals.
  • Python 2.6 beta: You can do like so: 0b1100111 or 0B1100111.
  • Python 2.6 beta: will also allow 0o27 or 0O27 (second character is the letter O) to represent an octal.
  • Python 3.0 beta: Same as 2.6, but will no longer allow the older 027 syntax for octals.

回答 0

供参考- 未来的 Python可能性:
从Python 2.6开始,您可以使用前缀0b0B表示二进制文字:

>>> 0b101111
47

您还可以使用新的bin函数来获取数字的二进制表示形式:

>>> bin(173)
'0b10101101'

文档的开发版本:Python 2.6的新增功能

For reference—future Python possibilities:
Starting with Python 2.6 you can express binary literals using the prefix 0b or 0B:

>>> 0b101111
47

You can also use the new bin function to get the binary representation of a number:

>>> bin(173)
'0b10101101'

Development version of the documentation: What’s New in Python 2.6


回答 1

>>> print int('01010101111',2)
687
>>> print int('11111111',2)
255

另一种方式。

>>> print int('01010101111',2)
687
>>> print int('11111111',2)
255

Another way.


回答 2

您如何用Python表达二进制文字?

它们不是“二进制”文字,而是“整数文字”。您可以用二进制格式表示整数文字,0后跟a Bb后跟一系列零和一,例如:

>>> 0b0010101010
170
>>> 0B010101
21

从Python 3 文档开始,以下是在Python中提供整数文字的方式:

整数文字由以下词汇定义描述:

integer      ::=  decinteger | bininteger | octinteger | hexinteger
decinteger   ::=  nonzerodigit (["_"] digit)* | "0"+ (["_"] "0")*
bininteger   ::=  "0" ("b" | "B") (["_"] bindigit)+
octinteger   ::=  "0" ("o" | "O") (["_"] octdigit)+
hexinteger   ::=  "0" ("x" | "X") (["_"] hexdigit)+
nonzerodigit ::=  "1"..."9"
digit        ::=  "0"..."9"
bindigit     ::=  "0" | "1"
octdigit     ::=  "0"..."7"
hexdigit     ::=  digit | "a"..."f" | "A"..."F"

除了可以存储在可用内存中的整数之外,整数字面量的长度没有限制。

请注意,不允许使用非零十进制数字开头的零。这是为了消除C样式八进制文字的歧义,Python在3.0版之前使用了这些文字。

整数文字的一些示例:

7     2147483647                        0o177    0b100110111
3     79228162514264337593543950336     0o377    0xdeadbeef
      100_000_000_000                   0b_1110_0101

在版本3.6中进行了更改:现在允许在文本中使用下划线进行分组。

其他表达二进制的方式:

您可以在可操作的字符串对象中包含零和一(尽管在大多数情况下,您可能应该对整数进行按位运算)-只需将零和一的字符串以及您要从中转换的基数传递给int ):

>>> int('010101', 2)
21

您可以选择使用0b0B前缀:

>>> int('0b0010101010', 2)
170

如果将其0作为基数传递,则如果字符串未指定前缀,则它将假定基数为10:

>>> int('10101', 0)
10101
>>> int('0b10101', 0)
21

从int转换回人类可读的二进制文件:

您可以将整数传递给bin以查看二进制文字的字符串表示形式:

>>> bin(21)
'0b10101'

你可以结合binint去来回:

>>> bin(int('010101', 2))
'0b10101'

如果希望最小宽度和前面的零,也可以使用格式规范:

>>> format(int('010101', 2), '{fill}{width}b'.format(width=10, fill=0))
'0000010101'
>>> format(int('010101', 2), '010b')
'0000010101'

How do you express binary literals in Python?

They’re not “binary” literals, but rather, “integer literals”. You can express integer literals with a binary format with a 0 followed by a B or b followed by a series of zeros and ones, for example:

>>> 0b0010101010
170
>>> 0B010101
21

From the Python 3 docs, these are the ways of providing integer literals in Python:

Integer literals are described by the following lexical definitions:

integer      ::=  decinteger | bininteger | octinteger | hexinteger
decinteger   ::=  nonzerodigit (["_"] digit)* | "0"+ (["_"] "0")*
bininteger   ::=  "0" ("b" | "B") (["_"] bindigit)+
octinteger   ::=  "0" ("o" | "O") (["_"] octdigit)+
hexinteger   ::=  "0" ("x" | "X") (["_"] hexdigit)+
nonzerodigit ::=  "1"..."9"
digit        ::=  "0"..."9"
bindigit     ::=  "0" | "1"
octdigit     ::=  "0"..."7"
hexdigit     ::=  digit | "a"..."f" | "A"..."F"

There is no limit for the length of integer literals apart from what can be stored in available memory.

Note that leading zeros in a non-zero decimal number are not allowed. This is for disambiguation with C-style octal literals, which Python used before version 3.0.

Some examples of integer literals:

7     2147483647                        0o177    0b100110111
3     79228162514264337593543950336     0o377    0xdeadbeef
      100_000_000_000                   0b_1110_0101

Changed in version 3.6: Underscores are now allowed for grouping purposes in literals.

Other ways of expressing binary:

You can have the zeros and ones in a string object which can be manipulated (although you should probably just do bitwise operations on the integer in most cases) – just pass int the string of zeros and ones and the base you are converting from (2):

>>> int('010101', 2)
21

You can optionally have the 0b or 0B prefix:

>>> int('0b0010101010', 2)
170

If you pass it 0 as the base, it will assume base 10 if the string doesn’t specify with a prefix:

>>> int('10101', 0)
10101
>>> int('0b10101', 0)
21

Converting from int back to human readable binary:

You can pass an integer to bin to see the string representation of a binary literal:

>>> bin(21)
'0b10101'

And you can combine bin and int to go back and forth:

>>> bin(int('010101', 2))
'0b10101'

You can use a format specification as well, if you want to have minimum width with preceding zeros:

>>> format(int('010101', 2), '{fill}{width}b'.format(width=10, fill=0))
'0000010101'
>>> format(int('010101', 2), '010b')
'0000010101'

回答 3

开头的0表示底数是8(而不是10),这很容易看到:

>>> int('010101', 0)
4161

如果您不以0开头,则python假定数字以10为底。

>>> int('10101', 0)
10101

0 in the start here specifies that the base is 8 (not 10), which is pretty easy to see:

>>> int('010101', 0)
4161

If you don’t start with a 0, then python assumes the number is base 10.

>>> int('10101', 0)
10101

回答 4

据我所知,直到2.5,Python仅支持十六进制和八进制文字。我确实找到了一些有关在将来的版本中添加二进制文件的讨论,但没有明确的定义。

As far as I can tell Python, up through 2.5, only supports hexadecimal & octal literals. I did find some discussions about adding binary to future versions but nothing definite.


回答 5

我很确定这是由于Python 3.0的变化之一,也许bin()与hex()和oct()一起使用。

编辑:lbrandy的答案在所有情况下都是正确的。

I am pretty sure this is one of the things due to change in Python 3.0 with perhaps bin() to go with hex() and oct().

EDIT: lbrandy’s answer is correct in all cases.


是否有标准化的方法可以在Python中交换两个变量?

问题:是否有标准化的方法可以在Python中交换两个变量?

在Python中,我已经看到使用此语法交换了两个变量值:

left, right = right, left

这是否被认为是交换两个变量值的标准方法,或者是否有其他一些方式可以按照惯例最通常地交换两个变量?

In Python, I’ve seen two variable values swapped using this syntax:

left, right = right, left

Is this considered the standard way to swap two variable values or is there some other means by which two variables are by convention most usually swapped?


回答 0

Python从左到右计算表达式。请注意,在评估分配时,右侧的评估先于左侧。

http://docs.python.org/3/reference/expressions.html#evaluation-order

这意味着该表达式的以下内容a,b = b,a

  • 对右侧b,a进行求值,即在内存中创建两个元素的元组。这两个元素是由标识符b和所指定的对象,这些对象a在程序执行期间对指令进行加密之前就已存在
  • 创建该元组后,仍未分配该元组对象,但这没关系,Python内部知道它在哪里
  • 然后,评估左侧,即将元组分配给左侧
  • 由于左侧是由两个标识符组成的,因此将元组解压缩,以便将第一个标识符a分配给元组的第一个元素(这是交换之前为b的对象,因为它具有名称b
    ,并且第二个标识符b分配给元组的第二个元素(该对象以前是交换之前的a,因为其标识符为a

该机制有效地交换了分配给标识符的对象,a并且b

因此,回答您的问题:是的,这是在两个对象上交换两个标识符的标准方法。
顺便说一下,对象不是变量,而是对象。

Python evaluates expressions from left to right. Notice that while evaluating an assignment, the right-hand side is evaluated before the left-hand side.

http://docs.python.org/3/reference/expressions.html#evaluation-order

That means the following for the expression a,b = b,a :

  • the right-hand side b,a is evaluated, that is to say a tuple of two elements is created in the memory. The two element are the objects designated by the identifiers b and a, that were existing before the instruction is encoutered during an execution of program
  • just after the creation of this tuple, no assignement of this tuple object have still been made, but it doesn’t matter, Python internally knows where it is
  • then, the left-hand side is evaluated, that is to say the tuple is assigned to the left-hand side
  • as the left-hand side is composed of two identifiers, the tuple is unpacked in order that the first identifier a be assigned to the first element of the tuple (which is the object that was formely b before the swap because it had name b)
    and the second identifier b is assigned to the second element of the tuple (which is the object that was formerly a before the swap because its identifiers was a)

This mechanism has effectively swapped the objects assigned to the identifiers a and b

So, to answer your question: YES, it’s the standard way to swap two identifiers on two objects.
By the way, the objects are not variables, they are objects.


回答 1

这是交换两个变量的标准方法,是的。

That is the standard way to swap two variables, yes.


回答 2

我知道三种交换变量的方法,但是a, b = b, a最简单。有

XOR(整数)

x = x ^ y
y = y ^ x
x = x ^ y

或简而言之,

x ^= y
y ^= x
x ^= y

临时变量

w = x
x = y
y = w
del w

元组交换

x, y = y, x

I know three ways to swap variables, but a, b = b, a is the simplest. There is

XOR (for integers)

x = x ^ y
y = y ^ x
x = x ^ y

Or concisely,

x ^= y
y ^= x
x ^= y

Temporary variable

w = x
x = y
y = w
del w

Tuple swap

x, y = y, x

回答 3

我不会说这是一种标准的交换方式,因为它将导致一些意外错误。

nums[i], nums[nums[i] - 1] = nums[nums[i] - 1], nums[i]

nums[i]将首先被修改,然后影响第二个变量nums[nums[i] - 1]

I would not say it is a standard way to swap because it will cause some unexpected errors.

nums[i], nums[nums[i] - 1] = nums[nums[i] - 1], nums[i]

nums[i] will be modified first and then affect the second variable nums[nums[i] - 1].


回答 4

不适用于多维数组,因为此处使用了引用。

import numpy as np

# swaps
data = np.random.random(2)
print(data)
data[0], data[1] = data[1], data[0]
print(data)

# does not swap
data = np.random.random((2, 2))
print(data)
data[0], data[1] = data[1], data[0]
print(data)

另请参见交换Numpy数组的切片

Does not work for multidimensional arrays, because references are used here.

import numpy as np

# swaps
data = np.random.random(2)
print(data)
data[0], data[1] = data[1], data[0]
print(data)

# does not swap
data = np.random.random((2, 2))
print(data)
data[0], data[1] = data[1], data[0]
print(data)

See also Swap slices of Numpy arrays


回答 5

为了解决eyquem解释的问题,您可以使用copy模块通过一个函数返回一个包含(反向)值副本的元组:

from copy import copy

def swapper(x, y):
  return (copy(y), copy(x))

与的功能相同lambda

swapper = lambda x, y: (copy(y), copy(x))

然后,将它们分配给所需的名称,如下所示:

x, y = swapper(y, x)

注意:如果需要,可以导入/使用deepcopy而不是copy

To get around the problems explained by eyquem, you could use the copy module to return a tuple containing (reversed) copies of the values, via a function:

from copy import copy

def swapper(x, y):
  return (copy(y), copy(x))

Same function as a lambda:

swapper = lambda x, y: (copy(y), copy(x))

Then, assign those to the desired names, like this:

x, y = swapper(y, x)

NOTE: if you wanted to you could import/use deepcopy instead of copy.


回答 6

您可以组合元组XOR交换:x,y = x ^ x ^ y,x ^ y ^ y

x, y = 10, 20

print('Before swapping: x = %s, y = %s '%(x,y))

x, y = x ^ x ^ y, x ^ y ^ y

print('After swapping: x = %s, y = %s '%(x,y))

要么

x, y = 10, 20

print('Before swapping: x = %s, y = %s '%(x,y))

print('After swapping: x = %s, y = %s '%(x ^ x ^ y, x ^ y ^ y))

使用lambda

x, y = 10, 20

print('Before swapping: x = %s, y = %s' % (x, y))

swapper = lambda x, y : ((x ^ x ^ y), (x ^ y ^ y))

print('After swapping: x = %s, y = %s ' % swapper(x, y))

输出:

Before swapping: x =  10 , y =  20
After swapping: x =  20 , y =  10

You can combine tuple and XOR swaps: x, y = x ^ x ^ y, x ^ y ^ y

x, y = 10, 20

print('Before swapping: x = %s, y = %s '%(x,y))

x, y = x ^ x ^ y, x ^ y ^ y

print('After swapping: x = %s, y = %s '%(x,y))

or

x, y = 10, 20

print('Before swapping: x = %s, y = %s '%(x,y))

print('After swapping: x = %s, y = %s '%(x ^ x ^ y, x ^ y ^ y))

Using lambda:

x, y = 10, 20

print('Before swapping: x = %s, y = %s' % (x, y))

swapper = lambda x, y : ((x ^ x ^ y), (x ^ y ^ y))

print('After swapping: x = %s, y = %s ' % swapper(x, y))

Output:

Before swapping: x =  10 , y =  20
After swapping: x =  20 , y =  10

Python中没有多行Lambda:为什么不呢?

问题:Python中没有多行Lambda:为什么不呢?

我听说它说不能在Python中添加多行lambda,因为它们会在语法上与Python中的其他语法结构发生冲突。我今天在公共汽车上考虑这个问题,意识到我想不出一个与多行lambda冲突的Python结构。考虑到我非常了解该语言,这令我感到惊讶。

现在,我确定Guido有一个理由不在语言中包含多行lambda,而是出于好奇:在什么情况下包含多行lambda会导致歧义?我所听到的是正确的,还是Python不允许多行lambda的其他原因?

I’ve heard it said that multiline lambdas can’t be added in Python because they would clash syntactically with the other syntax constructs in Python. I was thinking about this on the bus today and realized I couldn’t think of a single Python construct that multiline lambdas clash with. Given that I know the language pretty well, this surprised me.

Now, I’m sure Guido had a reason for not including multiline lambdas in the language, but out of curiosity: what’s a situation where including a multiline lambda would be ambiguous? Is what I’ve heard true, or is there some other reason that Python doesn’t allow multiline lambdas?


回答 0

请看以下内容:

map(multilambda x:
      y=x+1
      return y
   , [1,2,3])

这是lambda返回(y, [1,2,3])(因此映射仅获取一个参数,从而导致错误)吗?还是返回y?还是语法错误,因为新行上的逗号放置不正确?Python如何知道您想要什么?

在括号内,缩进对于python并不重要,因此您不能明确地使用多行。

这只是一个简单的例子,可能还有更多示例。

Look at the following:

map(multilambda x:
      y=x+1
      return y
   , [1,2,3])

Is this a lambda returning (y, [1,2,3]) (thus map only gets one parameter, resulting in an error)? Or does it return y? Or is it a syntax error, because the comma on the new line is misplaced? How would Python know what you want?

Within the parens, indentation doesn’t matter to python, so you can’t unambiguously work with multilines.

This is just a simple one, there’s probably more examples.


回答 1

Guido van Rossum(Python的发明者)自己在一个旧的博客文章中回答了这个确切的问题。
基本上,他承认这在理论上是可行的,但是任何建议的解决方案都是非Python的:

“但是,对于我来说,任何提议的解决方案的复杂性对我来说都是巨大的:它要求解析器(或更确切地说是词法分析器)能够在缩进敏感模式和缩进不敏感模式之间来回切换,并保持堆栈技术上可以解决所有问题(已经有一堆可以概括的缩进级别了。)但是,这些都不让我直觉,这简直就是鲁伯·戈德堡的精妙之处。”

Guido van Rossum (the inventor of Python) answers this exact question himself in an old blog post.
Basically, he admits that it’s theoretically possible, but that any proposed solution would be un-Pythonic:

“But the complexity of any proposed solution for this puzzle is immense, to me: it requires the parser (or more precisely, the lexer) to be able to switch back and forth between indent-sensitive and indent-insensitive modes, keeping a stack of previous modes and indentation level. Technically that can all be solved (there’s already a stack of indentation levels that could be generalized). But none of that takes away my gut feeling that it is all an elaborate Rube Goldberg contraption.”


回答 2

通常这很丑陋(但有时替代方法甚至更丑陋),因此一种解决方法是制作一个大括号表达式:

lambda: (
    doFoo('abc'),
    doBar(123),
    doBaz())

不过,它不会接受任何分配,因此您必须事先准备数据。我发现这个有用的地方是PySide包装器,有时在其中有简短的回调。编写其他成员函数将更加难看。通常,您将不需要此。

例:

pushButtonShowDialog.clicked.connect(
    lambda: (
    field1.clear(),
    spinBox1.setValue(0),
    diag.show())

This is generally very ugly (but sometimes the alternatives are even more ugly), so a workaround is to make a braces expression:

lambda: (
    doFoo('abc'),
    doBar(123),
    doBaz())

It won’t accept any assignments though, so you’ll have to prepare data beforehand. The place I found this useful is the PySide wrapper, where you sometimes have short callbacks. Writing additional member functions would be even more ugly. Normally you won’t need this.

Example:

pushButtonShowDialog.clicked.connect(
    lambda: (
    field1.clear(),
    spinBox1.setValue(0),
    diag.show())

回答 3

一些相关链接:

一段时间以来,我一直在关注Reia的开发,该开发最初也将基于Python的基于缩进的语法与Ruby块一起使用,全部都在Erlang之上。但是,设计师最终放弃了缩进敏感性,他写的有关该决定的文章包括有关他在缩进+多行块中遇到的问题的讨论,以及他对Guido的设计问题/决定的越来越多的赞赏:

http://www.unlimitednovelty.com/2009/03/indentation-sensitiveivity-post-mortem.html

另外,这是一个关于Python中Ruby样式块的有趣建议,我在Guido上发布了一个响应,但没有将其实际击倒(尽管不确定是否有任何后续击落):

http://tav.espians.com/ruby-style-blocks-in-python.html

A couple of relevant links:

For a while, I was following the development of Reia, which was initially going to have Python’s indentation based syntax with Ruby blocks too, all on top of Erlang. But, the designer wound up giving up on indentation sensitivity, and this post he wrote about that decision includes a discussion about problems he ran into with indentation + multi-line blocks, and an increased appreciation he gained for Guido’s design issues/decisions:

http://www.unlimitednovelty.com/2009/03/indentation-sensitivity-post-mortem.html

Also, here’s an interesting proposal for Ruby-style blocks in Python I ran across where Guido posts a response w/o actually shooting it down (not sure whether there has been any subsequent shoot down, though):

http://tav.espians.com/ruby-style-blocks-in-python.html


回答 4

[编辑]阅读此答案。它解释了为什么多行lambda不是问题。

简而言之,这是不可思议的。来自Guido van Rossum的博客文章:

我发现任何在表达式中间嵌入基于缩进的块的解决方案都是不可接受的。由于我发现语句分组的替代语法(例如花括号或begin / end关键字)同样不可接受,因此,这几乎使多行lambda成为无法解决的难题。

[Edit] Read this answer. It explains why multiline lambda is not a thing.

Simply put, it’s unpythonic. From Guido van Rossum’s blog post:

I find any solution unacceptable that embeds an indentation-based block in the middle of an expression. Since I find alternative syntax for statement grouping (e.g. braces or begin/end keywords) equally unacceptable, this pretty much makes a multi-line lambda an unsolvable puzzle.


回答 5

让我向您介绍一个光荣却可怕的技巧:

import types

def _obj():
  return lambda: None

def LET(bindings, body, env=None):
  '''Introduce local bindings.
  ex: LET(('a', 1,
           'b', 2),
          lambda o: [o.a, o.b])
  gives: [1, 2]

  Bindings down the chain can depend on
  the ones above them through a lambda.
  ex: LET(('a', 1,
           'b', lambda o: o.a + 1),
          lambda o: o.b)
  gives: 2
  '''
  if len(bindings) == 0:
    return body(env)

  env = env or _obj()
  k, v = bindings[:2]
  if isinstance(v, types.FunctionType):
    v = v(env)

  setattr(env, k, v)
  return LET(bindings[2:], body, env)

您现在可以按以下LET方式使用此表单:

map(lambda x: LET(('y', x + 1,
                   'z', x - 1),
                  lambda o: o.y * o.z),
    [1, 2, 3])

这使: [0, 3, 8]

Let me present to you a glorious but terrifying hack:

import types

def _obj():
  return lambda: None

def LET(bindings, body, env=None):
  '''Introduce local bindings.
  ex: LET(('a', 1,
           'b', 2),
          lambda o: [o.a, o.b])
  gives: [1, 2]

  Bindings down the chain can depend on
  the ones above them through a lambda.
  ex: LET(('a', 1,
           'b', lambda o: o.a + 1),
          lambda o: o.b)
  gives: 2
  '''
  if len(bindings) == 0:
    return body(env)

  env = env or _obj()
  k, v = bindings[:2]
  if isinstance(v, types.FunctionType):
    v = v(env)

  setattr(env, k, v)
  return LET(bindings[2:], body, env)

You can now use this LET form as such:

map(lambda x: LET(('y', x + 1,
                   'z', x - 1),
                  lambda o: o.y * o.z),
    [1, 2, 3])

which gives: [0, 3, 8]


回答 6

我在一些项目中实践这种肮脏的技巧感到内which,这有点简单:

    lambda args...:( expr1, expr2, expr3, ...,
            exprN, returnExpr)[-1]

我希望您能找到一种保持pythonic的方法,但是如果您必须这样做的话,那么会比使用exec和操作globals减轻痛苦。

I’m guilty of practicing this dirty hack in some of my projects which is bit simpler:

    lambda args...:( expr1, expr2, expr3, ...,
            exprN, returnExpr)[-1]

I hope you can find a way to stay pythonic but if you have to do it this less painful than using exec and manipulating globals.


回答 7

让我尝试解决@balpha解析问题。我会在多行lamda周围使用括号。如果没有括号,则lambda定义为贪婪的。所以lambda在

map(lambda x:
      y = x+1
      z = x-1
      y*z,
    [1,2,3]))

返回一个函数,该函数返回 (y*z, [1,2,3])

map((lambda x:
      y = x+1
      z = x-1
      y*z)
    ,[1,2,3]))

手段

map(func, [1,2,3])

其中func是返回y * z的多行lambda。那样有用吗?

Let me try to tackle @balpha parsing problem. I would use parentheses around the multiline lamda. If there is no parentheses, the lambda definition is greedy. So the lambda in

map(lambda x:
      y = x+1
      z = x-1
      y*z,
    [1,2,3]))

returns a function that returns (y*z, [1,2,3])

But

map((lambda x:
      y = x+1
      z = x-1
      y*z)
    ,[1,2,3]))

means

map(func, [1,2,3])

where func is the multiline lambda that return y*z. Does that work?


回答 8

(对于仍对该主题感兴趣的任何人。)

考虑一下这一点(即使在“多行” lambda中的其他语句中甚至使用了语句的返回值,尽管这很呕吐;-)

>>> def foo(arg):
...     result = arg * 2;
...     print "foo(" + str(arg) + ") called: " + str(result);
...     return result;
...
>>> f = lambda a, b, state=[]: [
...     state.append(foo(a)),
...     state.append(foo(b)),
...     state.append(foo(state[0] + state[1])),
...     state[-1]
... ][-1];
>>> f(1, 2);
foo(1) called: 2
foo(2) called: 4
foo(6) called: 12
12

(For anyone still interested in the topic.)

Consider this (includes even usage of statements’ return values in further statements within the “multiline” lambda, although it’s ugly to the point of vomiting ;-)

>>> def foo(arg):
...     result = arg * 2;
...     print "foo(" + str(arg) + ") called: " + str(result);
...     return result;
...
>>> f = lambda a, b, state=[]: [
...     state.append(foo(a)),
...     state.append(foo(b)),
...     state.append(foo(state[0] + state[1])),
...     state[-1]
... ][-1];
>>> f(1, 2);
foo(1) called: 2
foo(2) called: 4
foo(6) called: 12
12

回答 9

您可以简单地使用斜杠(\如果您的lambda函数有多行,则)

例:

mx = lambda x, y: x if x > y \
     else y
print(mx(30, 20))

Output: 30

You can simply use slash (\) if you have multiple lines for your lambda function

Example:

mx = lambda x, y: x if x > y \
     else y
print(mx(30, 20))

Output: 30

回答 10

我从python开始,但是来自Javascript最明显的方法是将表达式提取为函数…。

人为的例子,乘法表达式(x*2)被提取为函数,因此我可以使用多行:

def multiply(x):
  print('I am other line')
  return x*2

r = map(lambda x : multiply(x), [1, 2, 3, 4])
print(list(r))

https://repl.it/@datracka/python-lambda-function

也许它不能完全回答这个问题,即那是如何在lambda表达式本身中执行多行操作,但是如果有人得到此线程来查找如何调试该表达式(像我一样),我认为这会有所帮助

I am starting with python but coming from Javascript the most obvious way is extract the expression as a function….

Contrived example, multiply expression (x*2) is extracted as function and therefore I can use multiline:

def multiply(x):
  print('I am other line')
  return x*2

r = map(lambda x : multiply(x), [1, 2, 3, 4])
print(list(r))

https://repl.it/@datracka/python-lambda-function

Maybe it does not answer exactly the question if that was how to do multiline in the lambda expression itself, but in case somebody gets this thread looking how to debug the expression (like me) I think it will help


回答 11

关于丑陋的黑客,您始终可以使用exec和常规函数的组合来定义多行函数,如下所示:

f = exec('''
def mlambda(x, y):
    d = y - x
    return d * d
''', globals()) or mlambda

您可以将其包装为以下函数:

def mlambda(signature, *lines):
    exec_vars = {}
    exec('def mlambda' + signature + ':\n' + '\n'.join('\t' + line for line in lines), exec_vars)
    return exec_vars['mlambda']

f = mlambda('(x, y)',
            'd = y - x',
            'return d * d')

On the subject of ugly hacks, you can always use a combination of exec and a regular function to define a multiline function like this:

f = exec('''
def mlambda(x, y):
    d = y - x
    return d * d
''', globals()) or mlambda

You can wrap this into a function like:

def mlambda(signature, *lines):
    exec_vars = {}
    exec('def mlambda' + signature + ':\n' + '\n'.join('\t' + line for line in lines), exec_vars)
    return exec_vars['mlambda']

f = mlambda('(x, y)',
            'd = y - x',
            'return d * d')

回答 12

我只是在玩些游戏,以尝试对reduce进行字典理解,并提出这个内胆技巧:

In [1]: from functools import reduce
In [2]: reduce(lambda d, i: (i[0] < 7 and d.__setitem__(*i[::-1]), d)[-1], [{}, *{1:2, 3:4, 5:6, 7:8}.items()])                                                                                                                                                                 
Out[3]: {2: 1, 4: 3, 6: 5}

我只是想做与此Javascript dict理解相同的事情:https : //stackoverflow.com/a/11068265

I was just playing a bit to try to make a dict comprehension with reduce, and come up with this one liner hack:

In [1]: from functools import reduce
In [2]: reduce(lambda d, i: (i[0] < 7 and d.__setitem__(*i[::-1]), d)[-1], [{}, *{1:2, 3:4, 5:6, 7:8}.items()])                                                                                                                                                                 
Out[3]: {2: 1, 4: 3, 6: 5}

I was just trying to do the same as what was done in this Javascript dict comprehension: https://stackoverflow.com/a/11068265


回答 13

这是多行lambda的更有趣的实现。由于python如何使用缩进来构造代码,因此无法实现。

但是幸运的是,可以使用数组和括号禁用缩进格式。

正如已经指出的那样,您可以这样编写代码:

lambda args: (expr1, expr2,... exprN)

从理论上讲,如果可以保证从左到右进行求值,那么它会起作用,但是仍然会丢失从一个表达式传递到另一个表达式的值。

实现较为冗长的方法的一种方法是

lambda args: [lambda1, lambda2, ..., lambdaN]

每个lambda接收前一个参数的位置。

def let(*funcs):
    def wrap(args):
        result = args                                                                                                                                                                                                                         
        for func in funcs:
            if not isinstance(result, tuple):
                result = (result,)
            result = func(*result)
        return result
    return wrap

这种方法使您可以编写有点像Lisp / scheme的东西。

所以你可以这样写:

let(lambda x, y: x+y)((1, 2))

可以使用更复杂的方法来计算斜边

lst = [(1,2), (2,3)]
result = map(let(
  lambda x, y: (x**2, y**2),
  lambda x, y: (x + y) ** (1/2)
), lst)

这将返回一个标量数字列表,因此可用于将多个值减少为一个。

拥有那么多lambda肯定不会非常有效,但是如果您受到限制,那么它可能是快速完成某项工作并将其重写为实际函数的好方法。

Here’s a more interesting implementation of multi line lambdas. It’s not possible to achieve because of how python use indents as a way to structure code.

But luckily for us, indent formatting can be disabled using arrays and parenthesis.

As some already pointed out, you can write your code as such:

lambda args: (expr1, expr2,... exprN)

In theory if you’re guaranteed to have evaluation from left to right it would work but you still lose values being passed from one expression to an other.

One way to achieve that which is a bit more verbose is to have

lambda args: [lambda1, lambda2, ..., lambdaN]

Where each lambda receives arguments from the previous one.

def let(*funcs):
    def wrap(args):
        result = args                                                                                                                                                                                                                         
        for func in funcs:
            if not isinstance(result, tuple):
                result = (result,)
            result = func(*result)
        return result
    return wrap

This method let you write something that is a bit lisp/scheme like.

So you can write things like this:

let(lambda x, y: x+y)((1, 2))

A more complex method could be use to compute the hypotenuse

lst = [(1,2), (2,3)]
result = map(let(
  lambda x, y: (x**2, y**2),
  lambda x, y: (x + y) ** (1/2)
), lst)

This will return a list of scalar numbers so it can be used to reduce multiple values to one.

Having that many lambda is certainly not going to be very efficient but if you’re constrained it can be a good way to get something done quickly then rewrite it as an actual function later.


回答 14

因为lambda函数应该是单行的,因此它是函数的最简单形式, an entrance, then return

because a lambda function is supposed to be one-lined, as its the simplest form of a function, an entrance, then return


在函数调用中,星号运算符是什么意思?

问题:在函数调用中,星号运算符是什么意思?

*运算符在Python中是什么意思,例如likezip(*x)或代码f(**k)

  1. 在解释器内部如何处理?
  2. 它会影响性能吗?是快还是慢?
  3. 什么时候有用,什么时候没有?
  4. 应该在函数声明中还是在调用中使用它?

What does the * operator mean in Python, such as in code like zip(*x) or f(**k)?

  1. How is it handled internally in the interpreter?
  2. Does it affect performance at all? Is it fast or slow?
  3. When is it useful and when is it not?
  4. Should it be used in a function declaration or in a call?

回答 0

单颗星*将序列/集合解压缩为位置参数,因此您可以执行以下操作:

def sum(a, b):
    return a + b

values = (1, 2)

s = sum(*values)

这将打开元组的包装,使其实际执行为:

s = sum(1, 2)

双星**只使用字典并因此命名参数来做同样的事情:

values = { 'a': 1, 'b': 2 }
s = sum(**values)

您还可以结合:

def sum(a, b, c, d):
    return a + b + c + d

values1 = (1, 2)
values2 = { 'c': 10, 'd': 15 }
s = sum(*values1, **values2)

将执行为:

s = sum(1, 2, c=10, d=15)

另请参见Python文档的4.7.4-解包参数列表


另外,您可以定义要接受的函数*x**y参数,这使函数可以接受在声明中未专门命名的任何数量的位置和/或命名参数。

例:

def sum(*values):
    s = 0
    for v in values:
        s = s + v
    return s

s = sum(1, 2, 3, 4, 5)

或搭配**

def get_a(**values):
    return values['a']

s = get_a(a=1, b=2)      # returns 1

这可以使您无需声明它们即可指定大量可选参数。

再一次,您可以结合:

def sum(*values, **options):
    s = 0
    for i in values:
        s = s + i
    if "neg" in options:
        if options["neg"]:
            s = -s
    return s

s = sum(1, 2, 3, 4, 5)            # returns 15
s = sum(1, 2, 3, 4, 5, neg=True)  # returns -15
s = sum(1, 2, 3, 4, 5, neg=False) # returns 15

The single star * unpacks the sequence/collection into positional arguments, so you can do this:

def sum(a, b):
    return a + b

values = (1, 2)

s = sum(*values)

This will unpack the tuple so that it actually executes as:

s = sum(1, 2)

The double star ** does the same, only using a dictionary and thus named arguments:

values = { 'a': 1, 'b': 2 }
s = sum(**values)

You can also combine:

def sum(a, b, c, d):
    return a + b + c + d

values1 = (1, 2)
values2 = { 'c': 10, 'd': 15 }
s = sum(*values1, **values2)

will execute as:

s = sum(1, 2, c=10, d=15)

Also see section 4.7.4 – Unpacking Argument Lists of the Python documentation.


Additionally you can define functions to take *x and **y arguments, this allows a function to accept any number of positional and/or named arguments that aren’t specifically named in the declaration.

Example:

def sum(*values):
    s = 0
    for v in values:
        s = s + v
    return s

s = sum(1, 2, 3, 4, 5)

or with **:

def get_a(**values):
    return values['a']

s = get_a(a=1, b=2)      # returns 1

this can allow you to specify a large number of optional parameters without having to declare them.

And again, you can combine:

def sum(*values, **options):
    s = 0
    for i in values:
        s = s + i
    if "neg" in options:
        if options["neg"]:
            s = -s
    return s

s = sum(1, 2, 3, 4, 5)            # returns 15
s = sum(1, 2, 3, 4, 5, neg=True)  # returns -15
s = sum(1, 2, 3, 4, 5, neg=False) # returns 15

回答 1

一点:这些不是运算符。表达式中使用运算符从现有值创建新值(例如1 + 2变为3。这里的*和**是函数声明和调用语法的一部分。

One small point: these are not operators. Operators are used in expressions to create new values from existing values (1+2 becomes 3, for example. The * and ** here are part of the syntax of function declarations and calls.


回答 2

对于要“存储”函数调用的情况,我发现这特别有用。

例如,假设我对功能“ add”进行了一些单元测试:

def add(a, b): return a + b
tests = { (1,4):5, (0, 0):0, (-1, 3):3 }
for test, result in tests.items():
   print 'test: adding', test, '==', result, '---', add(*test) == result

除了手动执行类似add(test [0],test [1])之类的丑陋操作外,没有其他方法可以调用add。另外,如果变量数量可变,则所有需要的if语句的代码都会变得很丑陋。

另一个有用的地方是定义Factory对象(为您创建对象的对象)。假设您有一些工厂类,该类使Car对象返回。您可以使myFactory.make_car(’red’,’bmw’,’335ix’)创建Car(’red’,’bmw’,’335ix’),然后返回它。

def make_car(*args):
   return Car(*args)

当您要调用超类的构造函数时,这也很有用。

I find this particularly useful for when you want to ‘store’ a function call.

For example, suppose I have some unit tests for a function ‘add’:

def add(a, b): return a + b
tests = { (1,4):5, (0, 0):0, (-1, 3):3 }
for test, result in tests.items():
   print 'test: adding', test, '==', result, '---', add(*test) == result

There is no other way to call add, other than manually doing something like add(test[0], test[1]), which is ugly. Also, if there are a variable number of variables, the code could get pretty ugly with all the if-statements you would need.

Another place this is useful is for defining Factory objects (objects that create objects for you). Suppose you have some class Factory, that makes Car objects and returns them. You could make it so that myFactory.make_car(‘red’, ‘bmw’, ‘335ix’) creates Car(‘red’, ‘bmw’, ‘335ix’), then returns it.

def make_car(*args):
   return Car(*args)

This is also useful when you want to call a superclass’ constructor.


回答 3

它称为扩展调用语法。从文档中

如果语法* expression出现在函数调用中,则表达式必须计算为序列。来自此序列的元素被视为它们是附加的位置参数。如果存在位置参数x1,…,xN,并且表达式的计算结果为序列y1,…,yM,则等效于使用M + N个位置参数x1,…,xN,y1,…的调用。 ..,yM。

和:

如果语法** expression出现在函数调用中,则expression必须计算为一个映射,该映射的内容被视为其他关键字参数。如果关键字同时出现在表达式中并作为显式关键字参数出现,则会引发TypeError异常。

It is called the extended call syntax. From the documentation:

If the syntax *expression appears in the function call, expression must evaluate to a sequence. Elements from this sequence are treated as if they were additional positional arguments; if there are positional arguments x1,…, xN, and expression evaluates to a sequence y1, …, yM, this is equivalent to a call with M+N positional arguments x1, …, xN, y1, …, yM.

and:

If the syntax **expression appears in the function call, expression must evaluate to a mapping, the contents of which are treated as additional keyword arguments. In the case of a keyword appearing in both expression and as an explicit keyword argument, a TypeError exception is raised.


回答 4

在函数调用中,单星号将列表变成单独的参数(例如zip(*x),与zip(x1,x2,x3)if相同x=[x1,x2,x3]),而双星号将字典变成单独的关键字参数(例如f(**k),与f(x=my_x, y=my_y)if相同)k = {'x':my_x, 'y':my_y}

在函数定义中,反之亦然:单星将任意数量的参数转换为列表,而双引号将任意数量的关键字参数转换为字典。例如,def foo(*x)表示“ foo接受任意数量的参数,可以通过列表x来访问它们(即,如果用户调用foo(1,2,3)x将是[1,2,3])”,并且def bar(**k)表示“ bar可以接受任意数量的关键字参数,并且可以通过字典k进行访问。 (即,如果用户调用bar(x=42, y=23)k将为{'x': 42, 'y': 23})”。

In a function call the single star turns a list into seperate arguments (e.g. zip(*x) is the same as zip(x1,x2,x3) if x=[x1,x2,x3]) and the double star turns a dictionary into seperate keyword arguments (e.g. f(**k) is the same as f(x=my_x, y=my_y) if k = {'x':my_x, 'y':my_y}.

In a function definition it’s the other way around: the single star turns an arbitrary number of arguments into a list, and the double start turns an arbitrary number of keyword arguments into a dictionary. E.g. def foo(*x) means “foo takes an arbitrary number of arguments and they will be accessible through the list x (i.e. if the user calls foo(1,2,3), x will be [1,2,3])” and def bar(**k) means “bar takes an arbitrary number of keyword arguments and they will be accessible through the dictionary k (i.e. if the user calls bar(x=42, y=23), k will be {'x': 42, 'y': 23})”.


Python中的“ @”(@)符号有什么作用?

问题:Python中的“ @”(@)符号有什么作用?

我正在看一些使用 @符号的,但我不知道它的作用。我也不清楚要搜索的内容,因为搜索Python文档时会发现该@符号,否则Google不会返回相关结果。

I’m looking at some Python code which used the @ symbol, but I have no idea what it does. I also do not know what to search for as searching Python docs or Google does not return relevant results when the @ symbol is included.


回答 0

行首的@符号用于类,函数和方法修饰符

在这里阅读更多:

PEP 318:装饰器

Python装饰器

您会遇到的最常见的Python装饰器是:

@属性

@classmethod

@staticmethod

如果您@在一行的中间看到一个,那就是矩阵乘法。向下滚动以查看其他解决使用的答案@

An @ symbol at the beginning of a line is used for class, function and method decorators.

Read more here:

PEP 318: Decorators

Python Decorators

The most common Python decorators you’ll run into are:

@property

@classmethod

@staticmethod

If you see an @ in the middle of a line, that’s a different thing, matrix multiplication. Scroll down to see other answers that address that use of @.


回答 1

class Pizza(object):
    def __init__(self):
        self.toppings = []

    def __call__(self, topping):
        # When using '@instance_of_pizza' before a function definition
        # the function gets passed onto 'topping'.
        self.toppings.append(topping())

    def __repr__(self):
        return str(self.toppings)

pizza = Pizza()

@pizza
def cheese():
    return 'cheese'
@pizza
def sauce():
    return 'sauce'

print pizza
# ['cheese', 'sauce']

这表明在修饰符之后定义的function/ method/ 基本上只是作为符号传递到/ 之后的/ 。classargumentfunctionmethod@

第一次发现

微框架Flask从一开始就以以下格式引入装饰器

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

依次将其翻译为:

rule      = "/"
view_func = hello
# They go as arguments here in 'flask/app.py'
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
    pass

意识到这一点终于使我与Flask和平相处。

Example

class Pizza(object):
    def __init__(self):
        self.toppings = []

    def __call__(self, topping):
        # When using '@instance_of_pizza' before a function definition
        # the function gets passed onto 'topping'.
        self.toppings.append(topping())

    def __repr__(self):
        return str(self.toppings)

pizza = Pizza()

@pizza
def cheese():
    return 'cheese'
@pizza
def sauce():
    return 'sauce'

print pizza
# ['cheese', 'sauce']

This shows that the function/method/class you’re defining after a decorator is just basically passed on as an argument to the function/method immediately after the @ sign.

First sighting

The microframework Flask introduces decorators from the very beginning in the following format:

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

This in turn translates to:

rule      = "/"
view_func = hello
# They go as arguments here in 'flask/app.py'
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
    pass

Realizing this finally allowed me to feel at peace with Flask.


回答 2

此代码段:

def decorator(func):
   return func

@decorator
def some_func():
    pass

等效于以下代码:

def decorator(func):
    return func

def some_func():
    pass

some_func = decorator(some_func)

在装饰器的定义中,您可以添加一些通常不会由函数返回的修改内容。

This code snippet:

def decorator(func):
   return func

@decorator
def some_func():
    pass

Is equivalent to this code:

def decorator(func):
    return func

def some_func():
    pass

some_func = decorator(some_func)

In the definition of a decorator you can add some modified things that wouldn’t be returned by a function normally.


回答 3

在Python 3.5中,您可以重载@为运算符。之所以命名为__matmul__,是因为它被设计用于进行矩阵乘法,但是它可以是任何您想要的。有关详细信息,请参见PEP465

这是矩阵乘法的简单实现。

class Mat(list):
    def __matmul__(self, B):
        A = self
        return Mat([[sum(A[i][k]*B[k][j] for k in range(len(B)))
                    for j in range(len(B[0])) ] for i in range(len(A))])

A = Mat([[1,3],[7,5]])
B = Mat([[6,8],[4,2]])

print(A @ B)

此代码生成:

[[18, 14], [62, 66]]

In Python 3.5 you can overload @ as an operator. It is named as __matmul__, because it is designed to do matrix multiplication, but it can be anything you want. See PEP465 for details.

This is a simple implementation of matrix multiplication.

class Mat(list):
    def __matmul__(self, B):
        A = self
        return Mat([[sum(A[i][k]*B[k][j] for k in range(len(B)))
                    for j in range(len(B[0])) ] for i in range(len(A))])

A = Mat([[1,3],[7,5]])
B = Mat([[6,8],[4,2]])

print(A @ B)

This code yields:

[[18, 14], [62, 66]]

回答 4

Python中的“ @”(@)符号有什么作用?

简而言之,它用于装饰器语法和矩阵乘法。

在装饰器的上下文中,此语法为:

@decorator
def decorated_function():
    """this function is decorated"""

等效于此:

def decorated_function():
    """this function is decorated"""

decorated_function = decorator(decorated_function)

在矩阵乘法的上下文中,a @ b调用a.__matmul__(b)-使用以下语法:

a @ b

相当于

dot(a, b)

a @= b

相当于

a = dot(a, b)

其中dot,例如是numpy矩阵乘法函数,并且aand b是矩阵。

您如何独自发现呢?

我也不知道要搜索什么,因为搜索Python文档时会出现,或者当包含@符号时Google不会返回相关结果。

如果您想对某个特定的python语法有一个比较完整的了解,请直接查看语法文件。对于Python 3分支:

~$ grep -C 1 "@" cpython/Grammar/Grammar 

decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
decorators: decorator+
--
testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [',']
augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' |
            '<<=' | '>>=' | '**=' | '//=')
--
arith_expr: term (('+'|'-') term)*
term: factor (('*'|'@'|'/'|'%'|'//') factor)*
factor: ('+'|'-'|'~') factor | power

我们可以在这里看到@在三种情况下使用的内容:

  • 装饰工
  • 因子之间的运算符
  • 扩充的赋值运算符

装饰语法:

在Google上搜索“ decorator python docs”时,将“ Python语言参考”的“复合语句”部分作为最高结果之一。向下滚动至函数定义部分,我们可以通过搜索“ decorator”一词找到该部分,我们发现……有很多东西可供阅读。但是“ decorator”这个词是词汇表的链接,它告诉我们:

装饰工

返回另一个函数的函数,通常使用@wrapper语法将其用作函数转换。装饰器的常见示例是classmethod()staticmethod()

装饰器语法只是语法糖,以下两个函数定义在语义上是等效的:

def f(...):
    ...
f = staticmethod(f)

@staticmethod
def f(...):
    ...

类存在相同的概念,但在该类中较少使用。有关装饰器的更多信息,请参见函数定义和类定义的文档。

所以,我们看到

@foo
def bar():
    pass

在语义上与:

def bar():
    pass

bar = foo(bar)

它们并不完全相同,因为Python在装饰器(@)语法之前在bar之前评估foo表达式(可能是点分查找和函数调用),但在另一种情况下,则 bar 之后评估foo表达式。

(如果这种差异使代码的含义有所不同,则应重新考虑自己的生活,因为那会是病态的。)

堆叠式装饰器

如果我们回到函数定义语法文档,则会看到:

@f1(arg)
@f2
def func(): pass

大致相当于

def func(): pass
func = f1(arg)(f2(func))

这是一个演示,我们可以调用首先是装饰器的函数以及堆栈装饰器。在Python中,函数是一流的对象-这意味着您可以将一个函数作为参数传递给另一个函数,然后返回函数。装饰者可以做这两种事情。

如果我们堆叠装饰器,则已定义的函数会首先传递到紧接其上的装饰器,然后传递给下一个,依此类推。

这就总结了@装饰器上下文中的用法。

运营商, @

在语言参考的词法分析部分,我们有一个关于运算符部分,其中包括@,这使得它同时也是一个运算符:

以下标记是运算符:

+       -       *       **      /       //      %      @
<<      >>      &       |       ^       ~
<       >       <=      >=      ==      !=

在下一页的数据模型中,我们有模拟数字类型一节,

object.__add__(self, other)
object.__sub__(self, other) 
object.__mul__(self, other) 
object.__matmul__(self, other) 
object.__truediv__(self, other) 
object.__floordiv__(self, other)

[…]这些方法称为执行二进制算术运算(+-*@///,[…]

我们看到__matmul__对应于@。如果我们在文档中搜索“ matmul” ,则会在标题“ PEP 465-矩阵乘法的专用中缀运算符”下获得指向“ matmul”的Python 3.5新增功能的链接。

可以通过定义__matmul__()__rmatmul__()以及 __imatmul__()常规,反射和就地矩阵乘法来实现。

(所以现在我们了解到它@=是就地版本)。它进一步说明:

在数学,科学,工程学的许多领域中,矩阵乘法是一种常见的操作,并且@的加法允许编写更简洁的代码:

S = (H @ beta - r).T @ inv(H @ V @ H.T) @ (H @ beta - r)

代替:

S = dot((dot(H, beta) - r).T,
        dot(inv(dot(dot(H, V), H.T)), dot(H, beta) - r))

尽管可以重载该运算符以执行几乎所有操作,numpy例如,在中,我们将使用以下语法来计算数组和矩阵的内乘和外乘:

>>> from numpy import array, matrix
>>> array([[1,2,3]]).T @ array([[1,2,3]])
array([[1, 2, 3],
       [2, 4, 6],
       [3, 6, 9]])
>>> array([[1,2,3]]) @ array([[1,2,3]]).T
array([[14]])
>>> matrix([1,2,3]).T @ matrix([1,2,3])
matrix([[1, 2, 3],
        [2, 4, 6],
        [3, 6, 9]])
>>> matrix([1,2,3]) @ matrix([1,2,3]).T
matrix([[14]])

就地矩阵乘法: @=

在研究现有用法时,我们了解到还有就地矩阵乘法。如果尝试使用它,我们可能会发现尚未为numpy实现它:

>>> m = matrix([1,2,3])
>>> m @= m.T
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: In-place matrix multiplication is not (yet) supported. Use 'a = a @ b' instead of 'a @= b'.

实施后,我希望结果看起来像这样:

>>> m = matrix([1,2,3])
>>> m @= m.T
>>> m
matrix([[14]])

What does the “at” (@) symbol do in Python?

In short, it is used in decorator syntax and for matrix multiplication.

In the context of decorators, this syntax:

@decorator
def decorated_function():
    """this function is decorated"""

is equivalent to this:

def decorated_function():
    """this function is decorated"""

decorated_function = decorator(decorated_function)

In the context of matrix multiplication, a @ b invokes a.__matmul__(b) – making this syntax:

a @ b

equivalent to

dot(a, b)

and

a @= b

equivalent to

a = dot(a, b)

where dot is, for example, the numpy matrix multiplication function and a and b are matrices.

How could you discover this on your own?

I also do not know what to search for as searching Python docs or Google does not return relevant results when the @ symbol is included.

If you want to have a rather complete view of what a particular piece of python syntax does, look directly at the grammar file. For the Python 3 branch:

~$ grep -C 1 "@" cpython/Grammar/Grammar 

decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
decorators: decorator+
--
testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [',']
augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' |
            '<<=' | '>>=' | '**=' | '//=')
--
arith_expr: term (('+'|'-') term)*
term: factor (('*'|'@'|'/'|'%'|'//') factor)*
factor: ('+'|'-'|'~') factor | power

We can see here that @ is used in three contexts:

  • decorators
  • an operator between factors
  • an augmented assignment operator

Decorator Syntax:

A google search for “decorator python docs” gives as one of the top results, the “Compound Statements” section of the “Python Language Reference.” Scrolling down to the section on function definitions, which we can find by searching for the word, “decorator”, we see that… there’s a lot to read. But the word, “decorator” is a link to the glossary, which tells us:

decorator

A function returning another function, usually applied as a function transformation using the @wrapper syntax. Common examples for decorators are classmethod() and staticmethod().

The decorator syntax is merely syntactic sugar, the following two function definitions are semantically equivalent:

def f(...):
    ...
f = staticmethod(f)

@staticmethod
def f(...):
    ...

The same concept exists for classes, but is less commonly used there. See the documentation for function definitions and class definitions for more about decorators.

So, we see that

@foo
def bar():
    pass

is semantically the same as:

def bar():
    pass

bar = foo(bar)

They are not exactly the same because Python evaluates the foo expression (which could be a dotted lookup and a function call) before bar with the decorator (@) syntax, but evaluates the foo expression after bar in the other case.

(If this difference makes a difference in the meaning of your code, you should reconsider what you’re doing with your life, because that would be pathological.)

Stacked Decorators

If we go back to the function definition syntax documentation, we see:

@f1(arg)
@f2
def func(): pass

is roughly equivalent to

def func(): pass
func = f1(arg)(f2(func))

This is a demonstration that we can call a function that’s a decorator first, as well as stack decorators. Functions, in Python, are first class objects – which means you can pass a function as an argument to another function, and return functions. Decorators do both of these things.

If we stack decorators, the function, as defined, gets passed first to the decorator immediately above it, then the next, and so on.

That about sums up the usage for @ in the context of decorators.

The Operator, @

In the lexical analysis section of the language reference, we have a section on operators, which includes @, which makes it also an operator:

The following tokens are operators:

+       -       *       **      /       //      %      @
<<      >>      &       |       ^       ~
<       >       <=      >=      ==      !=

and in the next page, the Data Model, we have the section Emulating Numeric Types,

object.__add__(self, other)
object.__sub__(self, other) 
object.__mul__(self, other) 
object.__matmul__(self, other) 
object.__truediv__(self, other) 
object.__floordiv__(self, other)

[…] These methods are called to implement the binary arithmetic operations (+, -, *, @, /, //, […]

And we see that __matmul__ corresponds to @. If we search the documentation for “matmul” we get a link to What’s new in Python 3.5 with “matmul” under a heading “PEP 465 – A dedicated infix operator for matrix multiplication”.

it can be implemented by defining __matmul__(), __rmatmul__(), and __imatmul__() for regular, reflected, and in-place matrix multiplication.

(So now we learn that @= is the in-place version). It further explains:

Matrix multiplication is a notably common operation in many fields of mathematics, science, engineering, and the addition of @ allows writing cleaner code:

S = (H @ beta - r).T @ inv(H @ V @ H.T) @ (H @ beta - r)

instead of:

S = dot((dot(H, beta) - r).T,
        dot(inv(dot(dot(H, V), H.T)), dot(H, beta) - r))

While this operator can be overloaded to do almost anything, in numpy, for example, we would use this syntax to calculate the inner and outer product of arrays and matrices:

>>> from numpy import array, matrix
>>> array([[1,2,3]]).T @ array([[1,2,3]])
array([[1, 2, 3],
       [2, 4, 6],
       [3, 6, 9]])
>>> array([[1,2,3]]) @ array([[1,2,3]]).T
array([[14]])
>>> matrix([1,2,3]).T @ matrix([1,2,3])
matrix([[1, 2, 3],
        [2, 4, 6],
        [3, 6, 9]])
>>> matrix([1,2,3]) @ matrix([1,2,3]).T
matrix([[14]])

Inplace matrix multiplication: @=

While researching the prior usage, we learn that there is also the inplace matrix multiplication. If we attempt to use it, we may find it is not yet implemented for numpy:

>>> m = matrix([1,2,3])
>>> m @= m.T
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: In-place matrix multiplication is not (yet) supported. Use 'a = a @ b' instead of 'a @= b'.

When it is implemented, I would expect the result to look like this:

>>> m = matrix([1,2,3])
>>> m @= m.T
>>> m
matrix([[14]])

回答 5

Python中的“ @”(@)符号有什么作用?

@符号是python提供的语法糖decorator
以解释这个问题,这恰恰是关于decorator在Python中的作用?

简而言之,decorator您可以修改给定函数的定义,而无需触摸其最内层(即封闭)。
从第三方导入精美的包时,最常见的情况是。您可以可视化它,可以使用它,但不能触摸它的最内层和内心。

这是一个简单的示例,
假设我read_a_book在Ipython上定义了一个函数

In [9]: def read_a_book():
   ...:     return "I am reading the book: "
   ...: 
In [10]: read_a_book()
Out[10]: 'I am reading the book: '

你看,我忘了给它加一个名字。
如何解决这样的问题?当然,我可以将函数重新定义为:

def read_a_book():
    return "I am reading the book: 'Python Cookbook'"

但是,如果不允许我操作原始功能,或者要处理成千上万个此类功能,该怎么办。

通过不同的思维来解决问题并定义一个new_function

def add_a_book(func):
    def wrapper():
        return func() + "Python Cookbook"
    return wrapper

然后雇用它。

In [14]: read_a_book = add_a_book(read_a_book)
In [15]: read_a_book()
Out[15]: 'I am reading the book: Python Cookbook'

Tada,您知道,我在read_a_book不触及内部封闭的情况下进行了修改。没有什么可以阻止我装备decorator

关于什么 @

@add_a_book
def read_a_book():
    return "I am reading the book: "
In [17]: read_a_book()
Out[17]: 'I am reading the book: Python Cookbook'

@add_a_book这是一种花哨且方便的表达方式read_a_book = add_a_book(read_a_book),它是一种语法糖,没有比这更奇特的了。

What does the “at” (@) symbol do in Python?

@ symbol is a syntactic sugar python provides to utilize decorator,
to paraphrase the question, It’s exactly about what does decorator do in Python?

Put it simple decorator allow you to modify a given function’s definition without touch its innermost (it’s closure).
It’s the most case when you import wonderful package from third party. You can visualize it, you can use it, but you cannot touch its innermost and its heart.

Here is a quick example,
suppose I define a read_a_book function on Ipython

In [9]: def read_a_book():
   ...:     return "I am reading the book: "
   ...: 
In [10]: read_a_book()
Out[10]: 'I am reading the book: '

You see, I forgot to add a name to it.
How to solve such a problem? Of course, I could re-define the function as:

def read_a_book():
    return "I am reading the book: 'Python Cookbook'"

Nevertheless, what if I’m not allowed to manipulate the original function, or if there are thousands of such function to be handled.

Solve the problem by thinking different and define a new_function

def add_a_book(func):
    def wrapper():
        return func() + "Python Cookbook"
    return wrapper

Then employ it.

In [14]: read_a_book = add_a_book(read_a_book)
In [15]: read_a_book()
Out[15]: 'I am reading the book: Python Cookbook'

Tada, you see, I amended read_a_book without touching it inner closure. Nothing stops me equipped with decorator.

What’s about @

@add_a_book
def read_a_book():
    return "I am reading the book: "
In [17]: read_a_book()
Out[17]: 'I am reading the book: Python Cookbook'

@add_a_book is a fancy and handy way to say read_a_book = add_a_book(read_a_book), it’s a syntactic sugar, there’s nothing more fancier about it.


回答 6

如果您在使用Numpy库的python笔记本中引用某些代码,则@ operator表示矩阵乘法。例如:

import numpy as np
def forward(xi, W1, b1, W2, b2):
    z1 = W1 @ xi + b1
    a1 = sigma(z1)
    z2 = W2 @ a1 + b2
    return z2, a1

If you are referring to some code in a python notebook which is using Numpy library, then @ operator means Matrix Multiplication. For example:

import numpy as np
def forward(xi, W1, b1, W2, b2):
    z1 = W1 @ xi + b1
    a1 = sigma(z1)
    z2 = W2 @ a1 + b2
    return z2, a1

回答 7

从Python 3.5开始,“ @”用作MATRIX MULTIPLICATION(PEP 0465-参见https://www.python.org/dev/peps/pep-0465/)的专用中缀符号。

Starting with Python 3.5, the ‘@’ is used as a dedicated infix symbol for MATRIX MULTIPLICATION (PEP 0465 — see https://www.python.org/dev/peps/pep-0465/)


回答 8

在Python中添加了装饰器,以使函数和方法包装(接收函数并返回增强函数的函数)更易于阅读和理解。最初的用例是能够在定义的顶部将方法定义为类方法或静态方法。没有装饰器语法,将需要一个相当稀疏且重复的定义:

class WithoutDecorators:
def some_static_method():
    print("this is static method")
some_static_method = staticmethod(some_static_method)

def some_class_method(cls):
    print("this is class method")
some_class_method = classmethod(some_class_method)

如果将装饰器语法用于相同目的,则代码将更短且更易于理解:

class WithDecorators:
    @staticmethod
    def some_static_method():
        print("this is static method")

    @classmethod
    def some_class_method(cls):
        print("this is class method")

通用语法和可能的实现

装饰器通常是一个命名对象(不允许使用lambda表达式),该对象在被调用时将接受单个参数(它将成为装饰后的函数)并返回另一个可调用对象。此处使用“可调用”代替带有预想的“功能”。尽管装饰器通常在方法和功能的范围内进行讨论,但它们不限于此。实际上,任何可调用的对象(实现_call__方法的任何对象都被视为可调用对象)可以用作修饰符,并且它们返回的对象通常不是简单的函数,而是更多复杂类的实例,这些实例实现了自己的__call_方法。

装饰器语法只是一个语法糖。考虑以下装饰器用法:

@some_decorator
def decorated_function():
    pass

总是可以用显式的装饰器调用和函数重新分配来代替:

def decorated_function():
    pass
decorated_function = some_decorator(decorated_function)

但是,如果在单个函数上使用多个装饰器,则后者的可读性较低,并且也很难理解。可以以多种不同方式使用装饰器,如下所示:

作为功​​能

编写自定义装饰器的方法有很多,但是最简单的方法是编写一个函数,该函数返回包装原始函数调用的子函数。

通用模式如下:

def mydecorator(function):
    def wrapped(*args, **kwargs):
        # do some stuff before the original
        # function gets called
        result = function(*args, **kwargs)
        # do some stuff after function call and
        # return the result
        return result
    # return wrapper as a decorated function
    return wrapped

上课

尽管装饰器几乎总是可以使用函数来实现,但在某些情况下,使用用户定义的类是更好的选择。当装饰器需要复杂的参数化或取决于特定状态时,通常会发生这种情况。

非参数化装饰器作为类的通用模式如下:

class DecoratorAsClass:
    def __init__(self, function):
        self.function = function

    def __call__(self, *args, **kwargs):
        # do some stuff before the original
        # function gets called
        result = self.function(*args, **kwargs)
        # do some stuff after function call and
        # return the result
        return result

参数化装饰器

在实际代码中,经常需要使用可以参数化的装饰器。当将该函数用作装饰器时,解决方案很简单-必须使用第二层包装。这是装饰器的一个简单示例,该装饰器每次被调用都会重复执行装饰函数指定次数:

def repeat(number=3):
"""Cause decorated function to be repeated a number of times.

Last value of original function call is returned as a result
:param number: number of repetitions, 3 if not specified
"""
def actual_decorator(function):
    def wrapper(*args, **kwargs):
        result = None
        for _ in range(number):
            result = function(*args, **kwargs)
        return result
    return wrapper
return actual_decorator

通过这种方式定义的装饰器可以接受参数:

>>> @repeat(2)
... def foo():
...     print("foo")
...
>>> foo()
foo
foo

请注意,即使参数化装饰器的参数具有默认值,也必须在其名称后加上括号。使用带有默认参数的前面装饰器的正确方法如下:

>>> @repeat()
... def bar():
...     print("bar")
...
>>> bar()
bar
bar
bar

最后,让我们看看带有Properties的装饰器。

物产

这些属性提供了一个内置的描述符类型,该描述符类型知道如何将属性链接到一组方法。一个属性带有四个可选参数:fget,fset,fdel和doc。可以提供最后一个来定义链接到属性的文档字符串,就好像它是方法一样。这是一个Rectangle类的示例,可以通过直接访问存储两个角点的属性或使用width和height属性来控制它:

class Rectangle:
    def __init__(self, x1, y1, x2, y2):
        self.x1, self.y1 = x1, y1
        self.x2, self.y2 = x2, y2

    def _width_get(self):
        return self.x2 - self.x1

    def _width_set(self, value):
        self.x2 = self.x1 + value

    def _height_get(self):
        return self.y2 - self.y1

    def _height_set(self, value):
        self.y2 = self.y1 + value

    width = property(
        _width_get, _width_set,
        doc="rectangle width measured from left"
    )
    height = property(
        _height_get, _height_set,
        doc="rectangle height measured from top"
    )

    def __repr__(self):
        return "{}({}, {}, {}, {})".format(
            self.__class__.__name__,
            self.x1, self.y1, self.x2, self.y2
    )

创建属性的最佳语法是使用属性作为装饰器。这将减少类内部方法签名的数量,并使代码更具可读性和可维护性。使用装饰器,以上类变为:

class Rectangle:
    def __init__(self, x1, y1, x2, y2):
        self.x1, self.y1 = x1, y1
        self.x2, self.y2 = x2, y2

    @property
    def width(self):
        """rectangle height measured from top"""
        return self.x2 - self.x1

    @width.setter
    def width(self, value):
        self.x2 = self.x1 + value

    @property
    def height(self):
        """rectangle height measured from top"""
        return self.y2 - self.y1

    @height.setter
    def height(self, value):
        self.y2 = self.y1 + value

Decorators were added in Python to make function and method wrapping (a function that receives a function and returns an enhanced one) easier to read and understand. The original use case was to be able to define the methods as class methods or static methods on the head of their definition. Without the decorator syntax, it would require a rather sparse and repetitive definition:

class WithoutDecorators:
def some_static_method():
    print("this is static method")
some_static_method = staticmethod(some_static_method)

def some_class_method(cls):
    print("this is class method")
some_class_method = classmethod(some_class_method)

If the decorator syntax is used for the same purpose, the code is shorter and easier to understand:

class WithDecorators:
    @staticmethod
    def some_static_method():
        print("this is static method")

    @classmethod
    def some_class_method(cls):
        print("this is class method")

General syntax and possible implementations

The decorator is generally a named object ( lambda expressions are not allowed) that accepts a single argument when called (it will be the decorated function) and returns another callable object. “Callable” is used here instead of “function” with premeditation. While decorators are often discussed in the scope of methods and functions, they are not limited to them. In fact, anything that is callable (any object that implements the _call__ method is considered callable), can be used as a decorator and often objects returned by them are not simple functions but more instances of more complex classes implementing their own __call_ method.

The decorator syntax is simply only a syntactic sugar. Consider the following decorator usage:

@some_decorator
def decorated_function():
    pass

This can always be replaced by an explicit decorator call and function reassignment:

def decorated_function():
    pass
decorated_function = some_decorator(decorated_function)

However, the latter is less readable and also very hard to understand if multiple decorators are used on a single function. Decorators can be used in multiple different ways as shown below:

As a function

There are many ways to write custom decorators, but the simplest way is to write a function that returns a subfunction that wraps the original function call.

The generic patterns is as follows:

def mydecorator(function):
    def wrapped(*args, **kwargs):
        # do some stuff before the original
        # function gets called
        result = function(*args, **kwargs)
        # do some stuff after function call and
        # return the result
        return result
    # return wrapper as a decorated function
    return wrapped

As a class

While decorators almost always can be implemented using functions, there are some situations when using user-defined classes is a better option. This is often true when the decorator needs complex parametrization or it depends on a specific state.

The generic pattern for a nonparametrized decorator as a class is as follows:

class DecoratorAsClass:
    def __init__(self, function):
        self.function = function

    def __call__(self, *args, **kwargs):
        # do some stuff before the original
        # function gets called
        result = self.function(*args, **kwargs)
        # do some stuff after function call and
        # return the result
        return result

Parametrizing decorators

In real code, there is often a need to use decorators that can be parametrized. When the function is used as a decorator, then the solution is simple—a second level of wrapping has to be used. Here is a simple example of the decorator that repeats the execution of a decorated function the specified number of times every time it is called:

def repeat(number=3):
"""Cause decorated function to be repeated a number of times.

Last value of original function call is returned as a result
:param number: number of repetitions, 3 if not specified
"""
def actual_decorator(function):
    def wrapper(*args, **kwargs):
        result = None
        for _ in range(number):
            result = function(*args, **kwargs)
        return result
    return wrapper
return actual_decorator

The decorator defined this way can accept parameters:

>>> @repeat(2)
... def foo():
...     print("foo")
...
>>> foo()
foo
foo

Note that even if the parametrized decorator has default values for its arguments, the parentheses after its name is required. The correct way to use the preceding decorator with default arguments is as follows:

>>> @repeat()
... def bar():
...     print("bar")
...
>>> bar()
bar
bar
bar

Finally lets see decorators with Properties.

Properties

The properties provide a built-in descriptor type that knows how to link an attribute to a set of methods. A property takes four optional arguments: fget , fset , fdel , and doc . The last one can be provided to define a docstring that is linked to the attribute as if it were a method. Here is an example of a Rectangle class that can be controlled either by direct access to attributes that store two corner points or by using the width , and height properties:

class Rectangle:
    def __init__(self, x1, y1, x2, y2):
        self.x1, self.y1 = x1, y1
        self.x2, self.y2 = x2, y2

    def _width_get(self):
        return self.x2 - self.x1

    def _width_set(self, value):
        self.x2 = self.x1 + value

    def _height_get(self):
        return self.y2 - self.y1

    def _height_set(self, value):
        self.y2 = self.y1 + value

    width = property(
        _width_get, _width_set,
        doc="rectangle width measured from left"
    )
    height = property(
        _height_get, _height_set,
        doc="rectangle height measured from top"
    )

    def __repr__(self):
        return "{}({}, {}, {}, {})".format(
            self.__class__.__name__,
            self.x1, self.y1, self.x2, self.y2
    )

The best syntax for creating properties is using property as a decorator. This will reduce the number of method signatures inside of the class and make code more readable and maintainable. With decorators the above class becomes:

class Rectangle:
    def __init__(self, x1, y1, x2, y2):
        self.x1, self.y1 = x1, y1
        self.x2, self.y2 = x2, y2

    @property
    def width(self):
        """rectangle height measured from top"""
        return self.x2 - self.x1

    @width.setter
    def width(self, value):
        self.x2 = self.x1 + value

    @property
    def height(self):
        """rectangle height measured from top"""
        return self.y2 - self.y1

    @height.setter
    def height(self, value):
        self.y2 = self.y1 + value

回答 9

用其他方式说出别人的想法:是的,它是一个装饰器。

在Python中,就像:

  1. 创建一个函数(在@调用之后)
  2. 调用另一个函数以对您创建的函数进行操作。这将返回一个新函数。您调用的函数是@的参数。
  3. 用返回的新函数替换定义的函数。

这可以用于各种有用的东西,因为功能是对象,而只是指令而已,因此成为可能。

To say what others have in a different way: yes, it is a decorator.

In Python, it’s like:

  1. Creating a function (follows under the @ call)
  2. Calling another function to operate on your created function. This returns a new function. The function that you call is the argument of the @.
  3. Replacing the function defined with the new function returned.

This can be used for all kinds of useful things, made possible because functions are objects and just necessary just instructions.


回答 10

@符号还用于访问plydata / pandas数据框查询中的变量pandas.DataFrame.query。例:

df = pandas.DataFrame({'foo': [1,2,15,17]})
y = 10
df >> query('foo > @y') # plydata
df.query('foo > @y') # pandas

@ symbol is also used to access variables inside a plydata / pandas dataframe query, pandas.DataFrame.query. Example:

df = pandas.DataFrame({'foo': [1,2,15,17]})
y = 10
df >> query('foo > @y') # plydata
df.query('foo > @y') # pandas

回答 11

它表明您正在使用装饰器。这是Bruce Eckel在2008年的例子

It indicates that you are using a decorator. Here is Bruce Eckel’s example from 2008.