【问题标题】:How can I replicate the behavior of Python's sorted built-in function in C#?如何在 C# 中复制 Python 的 sorted 内置函数的行为?
【发布时间】:2014-01-30 01:21:22
【问题描述】:

我有一个 Python 字典列表。此列表在 Web 服务之间作为 json 传递。这些 Web 服务基于传递的 json 创建独特的签名。创建签名的一部分是规范化数据负载并确保一切都按正确的顺序进行,所以我正在这样做(在 Python 中)——效果很好。

data = [{'a': '1', 'b': '2', 'c': 3}, {'d': 3}, {3: 1}, {'100': '200'}]
sorted(data)
> [{3: 1}, {'100': '200'}, {'d': 3}, {'a': '1', 'c': 3, 'b': '2'}]

现在,我需要添加一个 C# 应用程序,它需要能够创建与 Python 代码完全相同的签名。我还没有发现像 Python 的 sorted 内置函数一样对上述数据结构进行排序的秘诀。

我正在使用ServiceStack 来解析 json 数据。

我希望它会像做这样的事情一样简单(在 C# 中):

var jsonPayload = "[{\"a\": \"1\", \"b\": \"2\", \"c\": 3}, {\"d\": 3}, {3: 1}, {\"100\": \"200\"}]";                                                                        
var parsedJson = JsonArrayObjects.Parse(jsonPayload);                                                                                                                        
parsedJson.Sort();  

但是,我从上面的 C# 代码中得到了这个异常:

`At least one object just implement IComparable`

我明白为什么会收到此错误,但我不确定我应该怎么做。我真的希望我不必推出自己的排序逻辑。我正在处理的实际数据是非常动态的。这只是阻碍我前进的一个例子。

是否有人对我如何在 C# 中进行排序以像 sorted python 函数一样为这种类型的嵌套数据结构提供任何建议或建议?

谢谢!

【问题讨论】:

  • 也许您可以使用基于内容的签名而不是 JSON 字符串?此外,当我尝试在 Python 3.2.1 中运行 sorted 命令时(没有用于排序的特定库),它显示 TypeError: unorderable types: dict() < dict()。这种比较是如何发生的?
  • 对不起,我应该说我使用的是 Python 2.7.5。
  • 这个现有问题可能对您有所帮助:stackoverflow.com/questions/736443/…
  • 你需要parsedJson.OrderBy(x => x, someComparator),其中someComparator 实现了与Python 2.7 的dict 的__cmp__ 相同的功能。我还不确定它是如何工作的,如果可以的话,我会研究它......
  • stackoverflow.com/questions/3484293/… 但是,该行为是未指定的,因此它可以并且可能会在实现之间非常频繁,因此我强烈建议不要将其用作您的规范化例程。我会做sorted(data.items()),因为它很明显并且记录了它的作用

标签: c# python sorting servicestack


【解决方案1】:

您可以解决它:从 C# 调用 Python 排序函数,因此您将获得完全相同的行为。

你可以使用IronPython:

Python 代码:

def Simple():
    print "Hello from Python"
    print "Call Dir(): "
    print dir()

C#代码:

using System;
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;

public class dynamic_demo
{
    static void Main()
    {
        var ipy = Python.CreateRuntime();
        dynamic test = ipy.UseFile("Test.py");
        test.Simple();
    }
}

完整示例here

http://blogs.msdn.com/b/charlie/archive/2009/10/25/hosting-ironpython-in-a-c-4-0-program.aspx

【讨论】:

    【解决方案2】:

    示例数据不是有效的 JSON,如图所示。整数三不能是键,它必须是字符串。将{3: 1} 更改为{"3": 1}

    第二个问题是默认情况下 C# 字典不可排序。但是,您可以将它们子类化以使它们可排序。

    Python2.x 的字典排序算法是:

    1) 如果字典大小不同,长度越短的值越小。

    2) 如果大小相同,则扫描第一个字典以查找第一个字典中不存在或在第二个字典中具有不匹配值的最小键。不匹配的值决定了哪个字典最大。

    以下是 Objects/dictobject.c 的 Python2.7 源代码的相关摘录:

    /* Subroutine which returns the smallest key in a for which b's value
       is different or absent.  The value is returned too, through the
       pval argument.  Both are NULL if no key in a is found for which b's status
       differs.  The refcounts on (and only on) non-NULL *pval and function return
       values must be decremented by the caller (characterize() increments them
       to ensure that mutating comparison and PyDict_GetItem calls can't delete
       them before the caller is done looking at them). */
    
    static PyObject *
    characterize(PyDictObject *a, PyDictObject *b, PyObject **pval)
    {
        PyObject *akey = NULL; /* smallest key in a s.t. a[akey] != b[akey] */
        PyObject *aval = NULL; /* a[akey] */
        Py_ssize_t i;
        int cmp;
    
        for (i = 0; i <= a->ma_mask; i++) {
            PyObject *thiskey, *thisaval, *thisbval;
            if (a->ma_table[i].me_value == NULL)
                continue;
            thiskey = a->ma_table[i].me_key;
            Py_INCREF(thiskey);  /* keep alive across compares */
            if (akey != NULL) {
                cmp = PyObject_RichCompareBool(akey, thiskey, Py_LT);
                if (cmp < 0) {
                    Py_DECREF(thiskey);
                    goto Fail;
                }
                if (cmp > 0 ||
                    i > a->ma_mask ||
                    a->ma_table[i].me_value == NULL)
                {
                    /* Not the *smallest* a key; or maybe it is
                     * but the compare shrunk the dict so we can't
                     * find its associated value anymore; or
                     * maybe it is but the compare deleted the
                     * a[thiskey] entry.
                     */
                    Py_DECREF(thiskey);
                    continue;
                }
            }
    
            /* Compare a[thiskey] to b[thiskey]; cmp <- true iff equal. */
            thisaval = a->ma_table[i].me_value;
            assert(thisaval);
            Py_INCREF(thisaval);   /* keep alive */
            thisbval = PyDict_GetItem((PyObject *)b, thiskey);
            if (thisbval == NULL)
                cmp = 0;
            else {
                /* both dicts have thiskey:  same values? */
                cmp = PyObject_RichCompareBool(
                                        thisaval, thisbval, Py_EQ);
                if (cmp < 0) {
                    Py_DECREF(thiskey);
                    Py_DECREF(thisaval);
                    goto Fail;
                }
            }
            if (cmp == 0) {
                /* New winner. */
                Py_XDECREF(akey);
                Py_XDECREF(aval);
                akey = thiskey;
                aval = thisaval;
            }
            else {
                Py_DECREF(thiskey);
                Py_DECREF(thisaval);
            }
        }
        *pval = aval;
        return akey;
    
    Fail:
        Py_XDECREF(akey);
        Py_XDECREF(aval);
        *pval = NULL;
        return NULL;
    }
    
    static int
    dict_compare(PyDictObject *a, PyDictObject *b)
    {
        PyObject *adiff, *bdiff, *aval, *bval;
        int res;
    
        /* Compare lengths first */
        if (a->ma_used < b->ma_used)
            return -1;              /* a is shorter */
        else if (a->ma_used > b->ma_used)
            return 1;               /* b is shorter */
    
        /* Same length -- check all keys */
        bdiff = bval = NULL;
        adiff = characterize(a, b, &aval);
        if (adiff == NULL) {
            assert(!aval);
            /* Either an error, or a is a subset with the same length so
             * must be equal.
             */
            res = PyErr_Occurred() ? -1 : 0;
            goto Finished;
        }
        bdiff = characterize(b, a, &bval);
        if (bdiff == NULL && PyErr_Occurred()) {
            assert(!bval);
            res = -1;
            goto Finished;
        }
        res = 0;
        if (bdiff) {
            /* bdiff == NULL "should be" impossible now, but perhaps
             * the last comparison done by the characterize() on a had
             * the side effect of making the dicts equal!
             */
            res = PyObject_Compare(adiff, bdiff);
        }
        if (res == 0 && bval != NULL)
            res = PyObject_Compare(aval, bval);
    
    Finished:
        Py_XDECREF(adiff);
        Py_XDECREF(bdiff);
        Py_XDECREF(aval);
        Py_XDECREF(bval);
        return res;
    }
    

    【讨论】:

      【解决方案3】:

      另一种选择是从 C# 应用程序中的命令行调用 Python 排序函数。你可以使用这个方法:

      private void run_cmd(string cmd, string args)
      {
           ProcessStartInfo start = new ProcessStartInfo();
           start.FileName = "my/full/path/to/python.exe";
           start.Arguments = string.Format("{0} {1}", cmd, args);
           start.UseShellExecute = false;
           start.RedirectStandardOutput = true;
           using(Process process = Process.Start(start))
           {
               using(StreamReader reader = process.StandardOutput)
               {
                   string result = reader.ReadToEnd();
                   Console.Write(result);
               }
           }
      }
      

      更多信息请访问:How do I run a Python script from C#?

      【讨论】:

        猜你喜欢
        • 2018-01-04
        • 1970-01-01
        • 1970-01-01
        • 2023-03-27
        • 2018-03-04
        • 2014-01-06
        • 1970-01-01
        • 2015-11-21
        相关资源
        最近更新 更多