8

Ruby to Python Translation Guide

 4 years ago
source link: https://fuzzyblog.io/blog/python/2019/12/11/ruby-to-python-translation-guide.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Last Update: 12/12/2019

I wrote this guide to different Ruby idioms and how to do them in Python. Organization is alphabetical. This will have to be a continually updated document as I keep finding holes in my Python knowledge and my prior practice of "Learn it / Blog it" simply doesn't put enough things into the same place. I need a single, easily searchable place to store all this random "how to do X cruft".

This is written as headings which discuss the problem and then how it is done in Ruby and in Python and then, optionally, a Python context. Hopefully this approach makes it useful to both ruby-ites and python-istas.

Note 1: My day to day lingua franca is still Ruby and in a Rails style context where everything is autoloaded. Python to me is a specialized tool for Data Science / Machine Learning. I find that Python:

  • Lacks the elegant simplicity of Ruby
  • Python syntax makes my eyes want to bleed, can we have an underscore character please, how about a colon or some more parentheses
  • Is developer hostile by default

Note 2: These are opinions! I am writing them after a craptastic work day so that and lack of sleep may be tilting my opinions more than a bit; I mean I'm right but still … And please understand that I fundamentally respect that amazing work that Guido did but I find myself in Matz's camp by preference. Still nothing else has the ecosystem that Python does.

Note 3: If you disagree with the above, my twitter handle is fuzzygroup and my email is easily discoverable. Let the fun begin …

Note 4: This was initially written in late 2019 so the Python flavor / version is 3.7 using VirtualEnv on a Mac with pip3.

Note 5: If there's things you want to see here then actually do let me know. This isn't a planned out document, it is more of a "what made me feel stooopid in Python today" type of thing.

Debugging

The ruby is:

byebug

The python is:

pdb.set_trace()

Getting All Attributes of an Object

Most Ruby objects, at least in a Rails context, are generally ActiveRecord objects and respond to .attributes so:

plan = Plan.first
2.6.3 :016 > plan.attributes
{
             "id" => 1,
     "created_at" => Sat, 27 Jul 2019 08:28:17 UTC +00:00,
     "updated_at" => Sat, 27 Jul 2019 08:28:17 UTC +00:00,
           "name" => "Write Daily",
    "public_name" => nil,
    "description" => nil,
        "options" => nil,
        "user_id" => 1,
    "category_id" => nil,
     "has_habits" => nil,
    "habit_count" => 3,
      "shareable" => false,
    "master_plan" => false,
         "active" => true
}

The python equivalent of this is is to reference the object's internal dict. Below we have an object called message which is coming in from a streaming data solution called sse:

message.__dict__
{'data': '{"all_awardings":[],"approved_at_utc":null,"ups":1,"user_reports":[]}', 'event': 'rc', 'id': '33288433929', 'retry': None}

# There are four different top level things that can be called:
message.data
message.event
message.id 
message.retry

# despite the appearance, the attributes of data are NOT json buy just a string so this doesn't work:

message.data.keys()
*** AttributeError: 'str' object has no attribute 'keys'

Getting All Methods for an Object

The ruby is:

hash = {'foo': 'bar', 'cat': 'blepp'}
(hash.methods - Object.methods).sort

(hash.methods - Object.methods).sort
[
    [  0] :[],
    [  1] :[]=,
    [  2] :all?,
    [  3] :any?,
    [  4] :assert_valid_keys,
    [  5] :assoc,
    [  6] :chain,
    [  7] :chart_json,
    [  8] :chunk,
    # a crap ton of methods omitted for clarity
]

# Also and BETTER since it just shows methods on the class; not inherited methods:

plan.public_methods(false)
[
    [ 0]                          active_habits()      Plan
    [ 1]                   after_add_for_habits()      Plan
    [ 2]                  after_add_for_habits=(val)   Plan
    [ 3]                  after_add_for_habits?()      Plan
    [ 4]                after_remove_for_habits()      Plan
    [ 5]               after_remove_for_habits=(val)   Plan
    [ 6]               after_remove_for_habits?()      Plan
    [ 7] autosave_associated_records_for_habits(*args) Plan
    [ 8]                  before_add_for_habits()      Plan
    [ 9]                 before_add_for_habits=(val)   Plan
    [10]                 before_add_for_habits?()      Plan
    [11]               before_remove_for_habits()      Plan
    [12]              before_remove_for_habits=(val)   Plan
    [13]              before_remove_for_habits?()      Plan
    [14]                                    foo(bar)   Plan
    [15] validate_associated_records_for_habits(*args) Plan
]

The python is:

dir(message)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'data', 'dump', 'event', 'id', 'parse', 'retry', 'sse_line_pattern']
(Pdb) message.sse_line_pattern
re.compile('(?P<name>[^:]*):?( ?(?P<value>.*))?')
# object.__dict__ gets you an object's data while dir(object) gets you an object's methods

See: Me, Lerner, Swlh, Stack Overflow

Getting All Keys in a Hash

The ruby is:

hash = {'foo': 'bar', 'cat': 'blepp'}
2.6.3 :018 > hash.keys
[
    [0] :foo,
    [1] :cat
]

The python is:

hash = {'foo': 'bar', 'cat': 'blepp'}
hash.keys()
dict_keys(['foo', 'cat'])

JSON Parsing

The ruby is:

json_str = '{"id": 12345, "message": "hello", "array": [1,2,3,4]}'
json = JSON.parse(json_str)
{
         "id" => 12345,
    "message" => "hello",
      "array" => [
        [0] 1,
        [1] 2,
        [2] 3,
        [3] 4
    ]
}

The python is:

import ujson as JSON

json_str = '{"id": 12345, "message": "hello", "array": [1,2,3,4]}'
json = JSON.loads(json_str)
{'id': 12345, 'message': 'hello', 'array': [1, 2, 3, 4]}
json.keys()
dict_keys(['id', 'message', 'array'])

I will confess to dipping into a bit of a Ruby idiom here by importing ujson as JSON (all caps). I find myself constantly making errors in python by calling my json variable json when that's actually an imported class / module. This one change makes things a lot easier (at least for me).

Length / Size of an Object

The ruby is:

plan.name.size
11

The python is:

len(message.data)
1826

Quitting the console

The ruby is:

quit

The python is:

quit()

Redis

The ruby is:

require 'redis'

2.6.3 :005 > redis = Redis.new
#<Redis client v4.1.3 for redis://127.0.0.1:6379/0>
2.6.3 :006 > redis.set("mykey", "hello world")
"OK"
2.6.3 :007 > redis.get("mykey")
"hello world"

The python is:

>>> import redis
>>> r = redis.Redis(host='localhost', port=6379, db=0)
>>> r.set('mykey', 'hello world')
True
>>> r.get('mykey')
'hello world'

String Interpolation

The ruby is:

skill = "beginner"
puts "Scott is a #{skill}"

The python is:

skill = "beginner"
print(f"Scott is a {skill}")

Try / & - Safe Navigation and Python Hashes

A ruby hash returns nil if the key specified is missing:

ENV['foo']
nil
2.3.1 :003 > ENV['RUBY_VERSION']
"ruby-2.3.1"

A python dict raises an exception:

import os

>>> os.environ['RAILS_ENV']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/sjohnson/Sync/fuzzygroup/adl/antihate/antihate_experts/venv/bin/../lib/python3.7/os.py", line 678, in __getitem__
    raise KeyError(key) from None
KeyError: 'RAILS_ENV'
>>> os.environ.get('RAILS_ENV')
>>> os.environ.get('RUBY_VERSION')
'ruby-2.3.1'

The ruby way around this would be to use try or the safe navigation operator:

https://stackoverflow.com/questions/46390057/from-ruby-to-python-is-there-an-equivalent-of-try

The Blank for Writing the Next Entry

The ruby is:

The python is:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK