减少Dart中包装对象的数量

时间:2014-03-19 11:47:03

标签: python oop python-2.7 dart wrapper

我的项目涉及将Python 2.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代码为:

import 'lib/inbuilts.dart';
import 'dart:io';

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

main() {
    stdout.writeln(fib(new $PyNum(36)));
}

代码工作正常但是在这样的代码中存在极端递归,在每个函数实例上创建的过多数量的包装器对象会严重影响代码的运行时间。例如,展开的Dart代码:

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的所有功能?非常感谢任何帮助!

3 个答案:

答案 0 :(得分:4)

我有类似的情况,我需要连接基本数据类型的附加信息,如String,int,double,...
我没有找到除了包装它们之外的解决方案。

您可以尝试通过

优化这些包装类
  • 使它们成为const(使用const构造函数
  • 尽可能将字段设为最终
  • 其他人可能

修改
- 你肯定想摆脱所有那些转换语句。

  1. 0.134285秒:没有包装
  2. 0.645971 sec:使用简化的构造函数,运算符&lt; =(见下文)
    使用const构造函数没有显着差异(转换为JS时更为重要)
  3. 1.449707秒:使用简化的构造函数
  4. 3.792590秒:您的代码
  5. 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);
    }
    

    另见:

答案 1 :(得分:2)

我还没有尝试过这个,但是如果永远不会包装对象,那么在Python中需要的任何方法如何将它作为一个独立的函数实现,而不是使用类型的情况。因此,两者之间的任何共同点都会全速运行。只在一个python类中实现的方法非常快,而且你只需要对Python中具有变形性的东西进行大量的类型测试。

或者只是在Dart中编写一个Python解释器。

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

答案 2 :(得分:1)

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

  1. 构造函数只包含值。如果您需要使用其他方法,例如。其他额外的构造函数。
  2. 分割比较运算符以减少不必要的类型检查。
  3. 算术运算符得到改进,添加了类型检查。
  4. Python类型组合成组$ PyType。这减少了类型检查。
  5. 从此示例中删除了不必要的代码。

    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();
    }