はじめに
堀川淳一郎先生のHoudiniチュートリアル「Spectrum Visualizer」を英語字幕化してみました。最近TouchDesignerやHoudiniベースのVJ映像にも興味を持ち始めたので。ネイティブライクですが日本人の英語ということで何となく聞き取りやすい気がします。約35分ある動画でHoudiniでのMIDIデータ制御の基礎を抑えるにはもってこいの内容で英語字幕化してみました。良ければご活用ください。
# Harmful book_info = ' The Three Musketeers: Alexandre Dumas' formatted_book_info = book_info.strip() formatted_book_info = formatted_book_info.upper() formatted_book_info = formatted_book_info.replace(':', ' by') # Idiomatic book_info = ' The Three Musketeers: Alexandre Dumas' formatted_book_info = book_info.strip().upper().replace(':', ' by')
# Harmful result_list = ['True', 'False', 'File not found'] result_string = '' for result in result_list: result_string += result # Idiomatic result_list = ['True', 'False', 'File not found'] result_string = ''.join(result_list)
# Harmful def get_formatted_user_info_worst(user): return 'Name: ' + user.name + 'Age: ' + str(user.age) + ', Sex: ' + user.sex def get_formatted_user_info_slightly_better(user): return 'Name: %s. Age: %i, Sex: %c' % (user.name, user.age, user.sex) # Idiomatic def get_formatted_user_info(user): output = 'Name: {user.name}, Age: {user.age}, Sex: {user.sex}'.format(user=user) return output
# Harmful some_other = list() some_list = list() for element in some_other_list: if is_prime(element): some_list.append(element + 5) # Idiomatic some_other_list = range(10) some_list = [element + 5 for element in some_other_list if is_prime(element)]
# Harmful # Pythonはswitch-case文がないので以下のようにしがちだが... def apply_operation(left_operand, right_operand, operator): if operator == '+': return left_operand + right_operand elif operator == '-': return left_operand - right_operand elif operator == '*': return left_operand * right_operand elif operator == '/': return left_operand / right_operand # Idiomatic def apply_operation(left_operand, right_operand, operator): import operator as op operator_mapper = {'+': op.add, '-': op.sub, '*': op.mul, '/': op.truediv} return operator_mapper[operator](left_operand, right_operand)
# Harmful log_severity = None if 'severity' in configuration: log_severity = configuration['severity'] else: log_severity = 'Info' # Idiomatic log_severity = configuration.get('severity', 'Info')
# Harmful user_email = {} for user in user_list: if user.email: user_email[user.name] = user.email # Idiomatic user_email = {user.name: user.email for user in user_list if user.email}
# Harmful users_first_names = set() for user in users: users_first_names.add(user.first_name) # Idiomatic users_first_names = {user.first_name for user in users}
# Harmful def print_employee_information(db_connection): db_cursor = db_connection.cursor() results = db_cursor.execute('select * from employees').fetchall() # 基本的にこの手法だと情報取得して出力は不可能 for row in results: print(row[1] + ', ' + row[0] + ' was hired on' \ + row[5] + ' (for $' + row[4] + ' per annum) info the' \ + row[2] + ' department and reports to ' + row[3]) # Idiomatic # 'employees'テーブルは次のカラムを持ってるのを前提とする # first_name, last_name, department, manager, salary, hire_date employee_row = namedtuple('EmployeeRow', \ ['first_name', 'last_name', 'department', 'manager', 'salary', 'hire_date']) EMPLOYEE_INFO_STRING = '{last}, {first} was hired on {date} \ ${sarlary} per annum) info the {department} department and reports to ager}' def print_employee_information(db_connection): db_cursor = db_connection.cursor() results = db_cursor.execute('select * from employees').fetchall() for row in results: employee = employee_row._make(row) # これでも間違った場所では出力はほとんど不可能ではある print(EMPLOYEE_INFO_STRING.format(\ last=employee.last_name,\ first=employee.first_name,\ date=employee.hire_date,\ salary=employee.salary,\ department=employee.department,\ manager=employee.manager))
# Harmful (name, age, temp, temp2) = get_user_info(user) # temp, temp2が使われていない if age > 21: output = '{name} can drink!'.format(name=name) # Idiomatic (name, age, _, _) = get_user_info(user) if age > 21: output = '{name} can drink!'.format(name=name)
# Harmful list_from_comma_separated_value_file = ['dog', 'Fido', 10] animal = list_from_comma_separated_value_file[0] name = list_from_comma_separated_value_file[1] age = list_from_comma_separated_value_file[2] output = ('{name} the {animal} is {age} years old'.format(animal=animal, name=name, age=age)) # Idiomatic list_from_comma_separated_value_file = ['dog', 'Fido', 10] (animal, name, age) = list_comma_separated_value_file output = ('{name} the {animal} is {age} years old'.format(animal=animal, name=name, age=age))
# Harmful from collections import Counter STATS_FORMAT = """Statistics: Mean: {mean} Median: {median} Mode: {mode}""" def calculate_mean(value_list): return float(sum(value_list) / len(value_list)) def calculate_median(value_list): return value_list(int(len(value_list) / 2)] def calculate_mode(value_list): return Counter(value_list).most_common(1)[0][0] values = [10, 20, 20, 30] mean = calculate_mean(values) median = calculate_median(values) mode = calculate_median(values) print(STATS_FORMAT.format(mean=mean, median=median, mode=mode)) # Idiomatic from collections import Counter STATS_FORMAT = """Statistics: Mean: {mean} Median: {median} Mode: {mode}""" def calculate_stastics(value_list): mean = float(sum(value_list) / len(value_list)) median = value_list[int(len(value_list) / 2)] mode = Counter(value_list).most_common[1)[0][0] return (mean, median, mode) (mean, median, mode) = calculate_stastics([10, 20, 20. 30]) print(STATS_FORMAT.format(mean=mean, median=median, mode=mode))
# Harmful class Product(): def __init__(self, name, price): self.name = name self.price = price # Idiomatic class Product(): def __init__(self, name, price): self.name self._price = price @property def price(self): return self._price * TAX_RATE @price.setter def price(self, value): # "setter"関数はpropertyと同名である必要がある self._price = value
# Harmful class Point(): def __init__(self, x, y): self.x = x self.y = y p = Point(1, 2) print(p) # 出力結果:'<__main__.Point object at 0x91ebd0>' # Idiomatic class Point(): def __init__(self, x, y): self.x = x self.y = y def __str__(self): return '{0}, {1}'.format(self.x, self.y) p = Point(1, 2) print(p) # 出力結果:'1, 2'
# Harmful # 例外発生したときにOpenしたファイルをCloseする術がない file_handle = open(path_to_file, 'r') for line in file_handle.readlines(): if raise_excepton(line): print('No! An Excepton!') # Idiomatic # context manager経由で開くと__enter__, __exit__メソッドを定義する事で容易に処理ができる with open(path_to_file, 'r') as file_handle: for line in file_handle: if raise_excepton(line): print('No! An Exception!')
# Harmful # list comprehensionは直ちに全要素を敷き詰めたリストを生成する # 膨大なリストの場合、非常に多くのメモリを食う事になる for uppercase_name in [name.upper() for name in get_all_usernames()]: process_normalized_username(uppercase_name) # Idiomatic # 一方、generator expressionの場合、要求に応じた要素が都度生成される for uppercase_name in (name_upper() for name in get_all_usernames()): process_normalized_username(uppercase_name)
# Harmful def get_twitter_stream_for_keyword(keyword): imaginary_twitter_api = ImaginaryTwitterAPI() if imaginary_twitter_api.can_get_stream_data(keyword): return imaginary_twitter_api.get_stream(keyword) current_stream = get_twitter_stream_for_keyword('#jeffknupp') for tweet in current_stream: process_tweet(tweet) def get_list_of_incredibly_complex_calculation_results(data): return [first_incredibly_long_calculation(data),\ second_incredibly_long_calculation(data),\ third_incredibly_long_calculation(data) ] # Idiomatic def get_twitter_stream_for_keyword(keyword): """この関数はgeneratorで'can_get_stream_data(user)'がFalse になるまで反復可能なデータを生成し続ける """ imaginary_twitter_api = ImaginaryTwitterAPI() while imaginary_twitter_api.can_get_stream_data(keyword): yield imaginary_twitter_api.get_stream(keyword) # generatorを呼び出してるので、クライアントが終了するまでプロセス続けてる for tweet in get_twitter_stream_for_keyword('#jeffknupp'): if got_stop_signal: break process_tweet(tweet) def get_list_of_incredibly_complex_calculation_results(data): yield first_incredibly_long_calculation(data) yield second_incredibly_long_calculation(data) yield third_incredibly_long_calculation(data)
# Harmful is_generic_name = False name = 'Tom' if name == 'Tom' or name == 'Dick' or name == 'Harry': is_generic_name = True # Idiomatic name = 'Tom' is_generic_name = name in ('Tom', 'Dick', 'Harry')
# Harmful for user in get_all_users(): has_malformed_email_address = False print ('Checking {}'.format(user)) for email_address in user.get_all_email_addresses(): if email_is_malformed(email_address): has_malformed_email_address = True print (u'email addressが不正な形式です!') break if not has_malformed_email_address: print ('All email address are valid!') # Idiomatic for user in get_all_users(): print ('Checking {}'.format(user)) for email_address in user.get_all_email_addresses(): if email_is_malformed(email_address): print(u'email addressが不正な形式です!') break else: print('All email address are valid')
# Harmful def make_api_call(foo, bar, baz): if baz in ('Unicorn', 'Oven', 'New York'): return foo(bar) else: return bar(foo) # I need to add another parameter to 'make_api_call' # without breaking everyone's existing code. # I have two options... def so_many_options(): # I can tack on new parameters, but only if I make # all of them optional ... def make_api_call(foo, bar, baz, qux=None, foo_polarity=None, baz_coefficient=None, quux_capacitor=None, file_not_fould=None): # ... and so on ad infinitum return file_not_found def version_graveyard(): # ... or I can create a new function each time the signature # hanges. def make_api_call_v2(foo, bar, baz, qux): return make_api_call(foo, bar, baz) - qux def make_api_call_v3(foo, bar, baz, qux, foo_polarity): if foo_polarity != 'reserved': return make_api_call_v2(foo, bar, baz, qux) return None def make_api_call_v4(foo, bar, baz, qux, foo_polarity, baz_coefficient): return make_api_call_v3(foo, bar, baz, qux, foo_polarity) * baz_coefficient def make_api_call_v5(foo, bar, baz, qux, foo_polarity, baz_coefficient, quux_capacitor): # I don't need 'foo', 'bar', or 'baz' anymore, but I have to keep # supporting them... return baz_coefficient * quux_capacitor # ... # Idiomatic def make_api_call(foo, bar, baz): if baz in ('Unicorn', 'Oven', 'New York'): return foo(bar) else: return bar(foo) # I need to add another parameter to 'make_api_call' # without breaking everyone's existing coe. # Easy ... def new_hotness(): def make_api_call(foo, bar, baz, *args, **kwargs): # Now I can accept any type and number of arguments # without worring about breaking existing code. baz_coefficient = kwargs['the_baz'] # I can even forward my args to a different fucntion # without knwoing their contents! return baz_coefficient in new_functions(args)
# Harmful def get_log_level(config_dict): if 'ENABLE_LOGGING' in config_dict: if config_dict['ENABLE_LOGGING'] != True: return None elif not 'DEFAULT_LOG_LEVEL' in config_dict: return None else: return None # Idiomatic def get_log_level(config_dict): try: if config_dict['ENABLE_LOGGING']: return config_dict['DEFAULT_LOG_LEVEL'] except KeyError: # if either value wasn't present, # a KeyError will be raised, so return None return None
# Harmful import requests def get_json_response(url): try: r = requests.get(url) return r.json() except: print('Oops, something went wrong!') return None # Idiomatic import requests def get_json_response(url): return requests.get(url).json() # If we need to make note of the exception, we # would write the function this way ... def alternate_get_json_response(url): try: r = requests.get(url) return r.json() except: # do some logging here, but don't handle the exception ... raise