减少D中包装对象的数量

2024-06-15 02:13:03 发布

您现在位置:Python中文网/ 问答频道 /正文

我的项目涉及到Python2.7代码到Dart代码的转换。为了完全模拟Python数据类型的所有特性,我在Dart中创建了包装器类,这些类扩展了原始Dart数据类型的功能,以匹配相应的Python类型。所有类型都有包装器,比如$PyNum代表数字,$PyString代表字符串等等。一切都很好,翻译的代码也很好。因为代码就像:

def fib(n):
    if n <= 2:
        return 1
    else:
        return fib (n - 1) + fib (n - 2)

print (fib(36))

相应生成的Dart代码为:

^{pr2}$

代码运行良好,但在类似这样的代码中,存在极端递归,在每个函数实例上创建的过多包装对象会严重影响代码的运行时间。例如,未包装的省道代码:

import'dart:io';

fib(n) {
    if (n <= 2) {
        return 1;
    } else {
        return (fib((n - 1)) + fib((n - 2)));
    }
}

main() {
    stdout.writeln(fib(36));
}

由于明显的原因,它的运行速度几乎比包装好的代码快15倍。所有涉及包装数据类型的计算都会返回该类的新实例。对我来说,通过Dart模拟Python在其数据类型中提供的所有特性是绝对重要的,而包装是我目前唯一想到的事情。我尝试使用单例类来创建一个用于计算的公共对象,但在递归和线程化的情况下失败了。在

我的$PyNum包装类是这样的:

class $PyNum {
    num _value;

    $PyNum(value) {
        switch ($getType(value)) {
            case 6:
                _value = value;
                break;
            case 7:
                try {
                    _value = num.parse(value);
                } catch (ex) {
                    print("Invalid string literal for num parsing");
                    exit(1);
                }
                break;
            case 5:
                _value = value.value();
                break;
            default:
                throw "Invalid input for num conversion";
        }
    }

    value() => _value;
    toString() => _value.toString();

    operator +(other) => new $PyNum(_value + other.value());
    operator -(other) => new $PyNum(_value - other.value());
    operator *(other) => new $PyNum(_value * other.value());
    operator ~/(other) => new $PyNum(_value ~/ other.value());
    operator |(other) => new $PyNum(_value | other.value());
    operator &(other) => new $PyNum(_value & other.value());
    operator ^(other) => new $PyNum(_value ^ other.value());
    operator %(other) => new $PyNum(_value % other.value());
    operator <<(other) => new $PyNum(_value << other.value());
    operator >>(other) => new $PyNum(_value >> other.value());
    operator ==(other) {
        switch ($getType(other)) {
            case 6:
                return _value == other;
            case 5:
                return _value == other.value();
            default:
                return false;
        }
    }
    operator <(other) {
        switch ($getType(other)) {
            case 6:
                return _value < other;
            case 5:
                return _value < other.value();
            default:
                return true;
        }
    }
    operator >(other) => !(this < other) && (this != other);
    operator <=(other) => (this < other) || (this == other);
    operator >=(other) => (this > other) || (this == other);
}

$getType(variable) {
    if (variable is bool)
        return 0;
    else if (variable is $PyBool)
        return 1;
    else if (variable is $PyDict)
        return 2;
    else if (variable is $PyList)
        return 3;
    else if (variable is List)
        return 4;
    else if (variable is $PyNum)
        return 5;
    else if (variable is num)
        return 6;
    else if (variable is $PyString)
        return 7;
    else if (variable is $PyTuple)
        return 8;
    else
        return -1;
}

const对象可以从这个类中提取出来吗?我不太清楚该怎么做。在

有没有其他方法可以有效地做到这一点,并且仍然能够模拟Python的所有特性?非常感谢任何帮助!在


Tags: 代码newreturnifisvaluethisvariable
3条回答

我有一个类似的情况,我需要将附加信息与基本数据类型(如String、int、double,…)相连接
除了包装它们,我没有找到解决办法。在

您可以尝试通过以下方法优化这些包装类

  • 使它们成为const(使用const构造函数
  • 在可能的情况下使字段最终化
  • 其他的也有可能

编辑
-你肯定想去掉所有的switch语句。在

  1. 0.134285秒:无包装
  2. 0.645971秒:使用简化的构造函数、运算符<;=(见下文)
    使用const构造函数并没有造成显著的区别(在转换为JS时可能更重要)
  3. 1.449707秒:使用简化的构造函数
  4. 3.792590秒:你的密码
class $PyNum2 {
  final num _value;

  const $PyNum2(this._value);

  factory $PyNum2.from(value) {
    switch ($getType2(value)) {
      case 6:
        return new $PyNum2(value);
        break;
      case 7:
        try {
          return new $PyNum2(num.parse(value));
        } catch (ex) {
          print("Invalid string literal for num parsing");
          exit(1);
        }
        break;
      case 5:
        return new $PyNum2(value.value());
        break;
      default:
        throw "Invalid input for num conversion";
    }
  }

  value() => _value;
  toString() => _value.toString();

  operator +(other) => new $PyNum2(_value + other.value());
  operator -(other) => new $PyNum2(_value - other.value());
  operator *(other) => new $PyNum2(_value * other.value());
  operator ~/(other) => new $PyNum2(_value ~/ other.value());
  operator %(other) => new $PyNum2(_value % other.value());
  operator ==(other) {
    switch ($getType2(other)) {
      case 6:
        return _value == other;
      case 5:
        return _value == other.value();
      default:
        return false;
    }
  }
  operator <(other) {
    switch ($getType2(other)) {
      case 6:
        return _value < other;
      case 5:
        return _value < other.value();
      default:
        return true;
    }
  }
  operator >(other) => !(this < other) && (this != other);
  operator <=(other) => this.value() <= other.value(); //(this < other) || (this == other);
  operator >=(other) => (this > other) || (this == other);
}

另请参见:

我没有尝试过这种方法,但是如果不包装对象,那么对于Python中需要的任何方法,可以使用类型case将其作为独立函数来实现。所以两者之间的任何共同点都会全速运转。只在一个python类中实现的方法会非常快,而且您只需要对python中的megamorphic进行大量类型测试。在

或者用Dart编写一个Python解释器。在

即使在您给出的示例中,如果使用const对象而不是每次都分配一个新的PyNum,也可能会做得更好。在

包装速度慢6.3倍的示例代码:

  1. 仅构造函数wrapps值。如果您需要conversion,请使用其他方法,例如其他额外的构造函数。在
  2. 拆分比较运算符以减少不必要的类型检查。在
  3. 改进了算术运算符,增加了类型检查。在
  4. Python类型组合成组$PyType。这样可以减少类型检查。在

从该示例中删除了不必要的代码。在

import 'dart:io';

fib(n) {
  if (n <= 2) {
    return 1;
  } else {
    return (fib((n - 1)) + fib((n - 2)));
  }
}

fib2(n) {
  if (n <= new $PyNum(2)) {
    return new $PyNum(1);
  } else {
    return (fib2((n - new $PyNum(1))) + fib2((n - new $PyNum(2))));
  }
}

main() {
  measure("fib", () => stdout.writeln(fib(42)));
  measure("fib2", () => stdout.writeln(fib2(new $PyNum(42))));
}

void measure(String msg, f()) {
  var sw = new Stopwatch();
  sw.start();
  f();
  sw.stop();
  print("$msg: ${sw.elapsedMilliseconds}");
}

class $PyTypes {
  static const $PyTypes NUM = const $PyTypes("NUM");

  final String name;

  const $PyTypes(this.name);
}

abstract class $PyType {
  $PyTypes get pyType;
}

class $PyNum extends $PyType {
  final int value;

  $PyTypes get pyType => $PyTypes.NUM;

  $PyNum(this.value);

  operator +(other) {
    if (other is $PyType) {
      switch (other.pyType) {
        case $PyTypes.NUM:
          $PyNum pyNum = other;
          return new $PyNum(value + pyNum.value);
      }
    } else if (other is int) {
      return new $PyNum(value + other);
    }

    throw new ArgumentError("other: $other");
  }

  operator -(other) {
    if (other is $PyType) {
      switch (other.pyType) {
        case $PyTypes.NUM:
          $PyNum pyNum = other;
          return new $PyNum(value - pyNum.value);
      }
    } else if (other is int) {
      return new $PyNum(value - other);
    }

    throw new ArgumentError("other: $other");
  }

  operator ==(other) {
    if (other is $PyType) {
      switch (other.pyType) {
        case $PyTypes.NUM:
          $PyNum pyNum = other;
          return value == pyNum.value;
      }
    } else if (other is int) {
      return value == other;
    }

    throw new ArgumentError("other: $other");
  }

  operator <(other) {
    if (other is $PyType) {
      switch (other.pyType) {
        case $PyTypes.NUM:
          $PyNum pyNum = other;
          return value < pyNum.value;
      }
    } else if (other is int) {
      return value < other;
    }

    throw new ArgumentError("other: $other");
  }

  operator <=(other) {
    if (other is $PyType) {
      switch (other.pyType) {
        case $PyTypes.NUM:
          $PyNum pyNum = other;
          return value <= pyNum.value;
      }
    } else if (other is int) {
      return value <= other;
    }

    throw new ArgumentError("other: $other");
  }

  String toString() => value.toString();
}

相关问题 更多 >