2009-09-03 29 views
177

Làm cách nào để thực hiện các biến biến trong Python?Làm cách nào để tạo một biến số biến?

Đây là một mục nhập thủ công elaborative, ví dụ: Variable variables

Tôi đã nghe nói đây là một ý tưởng tồi nói chung mặc dù, và nó là một lỗ hổng bảo mật trong Python. Điều đó có đúng không?

+16

đó là khía cạnh bảo dưỡng và sửa lỗi gây ra sự khủng khiếp. Hãy tưởng tượng cố gắng tìm ra nơi biến 'foo' thay đổi khi không có chỗ nào trong mã của bạn, nơi bạn thực sự thay đổi 'foo'. Hãy tưởng tượng thêm rằng đó là mã của người khác mà bạn phải duy trì ... OK, bạn có thể đi đến nơi hạnh phúc của bạn ngay bây giờ. –

+3

Sự cần thiết vẫn còn phát sinh, mặc dù. Tôi từng nghĩ rằng tôi cần phải làm điều này mọi lúc trước khi tôi gặp những ngôn ngữ lập trình thực sự. Những gợi ý tuyệt vời ở đây để chuyển sang tư duy lành mạnh hơn. –

+0

Bạn có thể thực hiện việc này trong trường hợp macro chậm. Đôi khi bạn phải, bởi vì kiểu dữ liệu duy nhất trong các macro SAS là chuỗi: -/ –

Trả lời

153

Bạn có thể sử dụng từ điển để thực hiện việc này. Từ điển là các kho khóa và giá trị.

>>> dct = {'x': 1, 'y': 2, 'z': 3} 
>>> dct 
{'y': 2, 'x': 1, 'z': 3} 
>>> dct["y"] 
2 

Bạn có thể sử dụng tên khóa biến để đạt được hiệu quả của biến số không có rủi ro bảo mật.

>>> x = "spam" 
>>> z = {x: "eggs"} 
>>> z["spam"] 
'eggs' 

Đối với trường hợp bạn đang nghĩ đến việc làm một cái gì đó giống như

var1 = 'foo' 
var2 = 'bar' 
var3 = 'baz' 
... 

một danh sách có thể thích hợp hơn một dict. Một danh sách đại diện cho một chuỗi các lệnh của các đối tượng, với chỉ số nguyên:

l = ['foo', 'bar', 'baz'] 
print(l[1])   # prints bar, because indices start at 0 
l.append('potatoes') # l is now ['foo', 'bar', 'baz', 'potatoes'] 

Đối với chuỗi lệnh, danh sách được thuận tiện hơn dicts với các phím số nguyên, bởi vì danh sách hỗ trợ lặp để chỉ mục, slicing, append, và các hoạt động khác mà sẽ yêu cầu quản lý khóa khó xử với một dict.

+2

Tôi e rằng mình không hiểu rõ lắm. Bạn có thể giải thích? –

+0

http://docs.python.org/library/stdtypes.html#dict –

+0

Đã chỉnh sửa câu trả lời của tôi để giải thích từ điển. –

26

Bất cứ khi nào bạn muốn sử dụng biến số biến, tốt hơn bạn nên sử dụng từ điển. Vì vậy, thay vì viết

$foo = "bar" 
$$foo = "baz" 

bạn viết

mydict = {} 
foo = "bar" 
mydict[foo] = "baz" 

Bằng cách này bạn sẽ không vô tình ghi đè lên các biến tồn tại trước đó (đó là khía cạnh bảo mật) và bạn có thể có "không gian tên" khác nhau.

54

Sử dụng chức năng getattr được tích hợp sẵn để nhận thuộc tính trên một đối tượng theo tên. Sửa đổi tên khi cần thiết.

obj.spam = 'eggs' 
name = 'spam' 
getattr(obj, name) # returns 'eggs' 
+0

Điều đó hoạt động tuyệt vời với tên gọi là – kap

+0

Đáng yêu. Đây chính xác là những gì tôi cần. Tôi có một đối tượng và cần thiết để truy cập các biến bằng một chuỗi và tôi không muốn sử dụng 'eval'. Cảm ơn! – rayryeng

49

Không phải là ý hay. Nếu bạn đang truy cập một biến toàn cục, bạn có thể sử dụng globals().

>>> a = 10 
>>> globals()['a'] 
10 

Nếu bạn muốn truy cập vào một biến trong phạm vi địa phương mà bạn có thể sử dụng locals(), nhưng bạn không thể gán giá trị cho các dict trả lại.

Giải pháp tốt hơn là sử dụng getattr hoặc lưu biến của bạn trong từ điển và sau đó truy cập chúng theo tên.

+11

+1 trả lời câu hỏi như đã nêu (mặc dù nó thật khủng khiếp) – bobince

+7

Đừng quên đề cập đến việc bạn không thể sửa đổi biến thông qua người dân địa phương() (http://docs.python.org/library/functions.html#locals). –

+0

Bạn không thể đặt biến theo cách này. Bạn không thể làm 'globals() ['a'] = 10'. – sudo

1

Sự đồng thuận là sử dụng từ điển cho việc này - xem các câu trả lời khác. Tuy nhiên, đây là một ý tưởng hay cho hầu hết các trường hợp, có nhiều khía cạnh phát sinh từ việc này:

  • bạn sẽ tự chịu trách nhiệm cho từ điển này, bao gồm thu gom rác thải (biến in-dict), v.v.
  • có hoặc không có địa phương hoặc tính toàn cầu cho các biến biến, nó phụ thuộc vào tính toàn cầu của từ điển
  • nếu bạn muốn đổi tên một tên biến, bạn sẽ phải làm điều đó bằng tay
  • tuy nhiên, bạn được nhiều hơn nữa linh hoạt, ví dụ
    • bạn có thể quyết định để ghi đè lên các biến hiện tại hoặc ...
    • ... chọn để thực hiện các biến const
    • để nâng cao một ngoại lệ về ghi đè lên với nhiều loại khác nhau
    • , vv

Điều đó nói rằng, tôi đã triển khai lớp variable variables manager cung cấp một số ý tưởng ở trên. Nó hoạt động cho python 2 và 3.

Bạn muốn sử dụng the class như thế này:

from variableVariablesManager import VariableVariablesManager 

myVars = VariableVariablesManager() 
myVars['test'] = 25 
print(myVars['test']) 

# define a const variable 
myVars.defineConstVariable('myconst', 13) 
try: 
    myVars['myconst'] = 14 # <- this raises an error, since 'myconst' must not be changed 
    print("not allowed") 
except AttributeError as e: 
    pass 

# rename a variable 
myVars.renameVariable('myconst', 'myconstOther') 

# preserve locality 
def testLocalVar(): 
    myVars = VariableVariablesManager() 
    myVars['test'] = 13 
    print("inside function myVars['test']:", myVars['test']) 
testLocalVar() 
print("outside function myVars['test']:", myVars['test']) 

# define a global variable 
myVars.defineGlobalVariable('globalVar', 12) 
def testGlobalVar(): 
    myVars = VariableVariablesManager() 
    print("inside function myVars['globalVar']:", myVars['globalVar']) 
    myVars['globalVar'] = 13 
    print("inside function myVars['globalVar'] (having been changed):", myVars['globalVar']) 
testGlobalVar() 
print("outside function myVars['globalVar']:", myVars['globalVar']) 

Nếu bạn muốn cho phép ghi đè các biến với chỉ cùng loại:

myVars = VariableVariablesManager(enforceSameTypeOnOverride = True) 
myVars['test'] = 25 
myVars['test'] = "Cat" # <- raises Exception (different type on overwriting) 
+0

Thoạt nhìn, các hàng nhập khẩu lạc quan dài đã khiến tôi nghĩ rằng đây là Java. – markroxor

6

Bạn có sử dụng globals() built in method để đạt được hành vi đó:

def var_of_var(k, v): 
    globals()[k] = v 

print variable_name # NameError: name 'variable_name' is not defined 
some_name = 'variable_name' 
globals()[some_name] = 123 
print variable_name # 123 

some_name = 'variable_name2' 
var_of_var(some_name, 456) 
print variable_name2 # 456 
8

Thay vì từ điển bạn cũng có thể sử dụng têntuple từ mô-đun bộ sưu tập, giúp truy cập dễ dàng hơn.

ví dụ:

#using dictionary 
variables = {} 
variables["first"] = 34 
variables["second"] = 45 
print variables["first"], variables["second"] 

#using namedtuple 
Variables = namedtuple('Variables', ['first', 'second']) 
vars = Variables(34, 45) 
print vars.first, vars.second 
11

lập trình mới đôi khi viết mã như thế này:

my_calculator.button_0 = tkinter.Button(root, text=0) 
my_calculator.button_1 = tkinter.Button(root, text=1) 
my_calculator.button_2 = tkinter.Button(root, text=2) 
... 

Các coder sau đó trái với một đống biến được đặt tên, với một nỗ lực mã hóa của O (m * n), trong đó m là số biến được đặt tên và n là số lần Đó là nhóm các biến cần được truy cập (bao gồm cả việc tạo). Người mới bắt đầu quan sát hơn rằng sự khác biệt duy nhất trong mỗi dòng là một số thay đổi dựa trên quy tắc và quyết định sử dụng vòng lặp. Tuy nhiên, họ gặp khó khăn về cách tự động tạo các tên biến đó và có thể thử một cái gì đó như thế này:

for i in range(10): 
    my_calculator.('button_%d' % i) = tkinter.Button(root, text=i) 

Họ sớm nhận thấy điều này không hiệu quả.

Nếu chương trình yêu cầu biến tùy ý "tên", từ điển là lựa chọn tốt nhất, như được giải thích trong các câu trả lời khác. Tuy nhiên, nếu bạn chỉ đơn giản là cố gắng để tạo ra nhiều biến và bạn không nhớ đề cập đến chúng với một chuỗi các số nguyên, có thể bạn đang tìm kiếm một list. Điều này đặc biệt đúng nếu dữ liệu của bạn đồng nhất, chẳng hạn như chỉ số nhiệt độ hàng ngày, điểm số bài kiểm tra hàng tuần hoặc một mạng lưới các tiện ích đồ họa.

Điều này có thể được lắp ráp như sau:

my_calculator.buttons = [] 
for i in range(10): 
    my_calculator.buttons.append(tkinter.Button(root, text=i)) 

list này cũng có thể được tạo ra trong một dòng với một sự hiểu biết:

my_calculator.buttons = [tkinter.Button(root, text=i) for i in range(10)] 

Kết quả trong cả hai trường hợp là một dân list, với phần tử đầu tiên được truy cập với my_calculator.buttons[0], tiếp theo với my_calculator.buttons[1], v.v. Tên biến "cơ sở" trở thành tên của list và số nhận dạng khác nhau được sử dụng để truy cập nó.

Cuối cùng, đừng quên các cấu trúc dữ liệu khác, chẳng hạn như set - điều này tương tự như từ điển, ngoại trừ mỗi "tên" không có giá trị gắn liền với nó. Nếu bạn chỉ cần một "túi" của các đối tượng, điều này có thể là một lựa chọn tuyệt vời. Thay vì một cái gì đó như thế này:

keyword_1 = 'apple' 
keyword_2 = 'banana' 

if query == keyword_1 or query == keyword_2: 
    print('Match.') 

Bạn sẽ có điều này:

keywords = {'apple', 'banana'} 
if query in keywords: 
    print('Match.') 

Sử dụng một list cho một chuỗi các đối tượng tương tự, một set cho một túi tùy tiện theo lệnh của các đối tượng, hoặc một dict cho một túi tên có giá trị liên quan.

1

Tôi đang trả lời câu hỏi: How to get the value of a variable given its name in a string? bị đóng với liên kết đến câu hỏi này.

Nếu các biến được đề cập là một phần của đối tượng (một phần của một lớp), thì một số hàm hữu ích để đạt được chính xác là hasattr, getattrsetattr.

Vì vậy, ví dụ bạn có thể có:

class Variables(object): 
    def __init__(self): 
     self.foo = "initial_variable" 
    def create_new_var(self,name,value): 
     setattr(self,name,value) 
    def get_var(self,name): 
     if hasattr(self,name): 
      return getattr(self,name) 
     else: 
      raise("Class does not have a variable named: "+name) 

Sau đó, bạn có thể làm:

v = Variables() 
v.get_var("foo") 

"initial_variable"

v.create_new_var(v.foo,"is actually not initial") 
v.initial_variable 

"thực sự không phải là ban đầu"

1

Bất kỳ tập hợp biến nào cũng có thể được bao bọc trong một lớp. Biến "Biến" có thể được thêm vào thể hiện lớp trong suốt thời gian chạy bằng cách truy cập trực tiếp từ điển được tích hợp thông qua thuộc tính __dict__.

Đoạn mã sau định nghĩa lớp Biến, trong đó thêm biến (trong trường hợp này thuộc tính) vào thể hiện của nó trong khi xây dựng. tên biến được lấy từ một danh mục quy định (trong đó, ví dụ, có thể đã được tạo ra bởi mã chương trình):

# some list of variable names 
L = ['a', 'b', 'c'] 

class Variables: 
    def __init__(self, L): 
     for item in L: 
      self.__dict__[item] = 100 

v = Variables(L) 
print(v.a, v.b, v.c) 
#will produce 100 100 100 
1

Lớp SimpleNamespace có thể được sử dụng để tạo các thuộc tính mới với setattr, hoặc lớp con SimpleNamespace và tạo riêng của bạn để thêm tên thuộc tính mới (biến).

from types import SimpleNamespace 

variables = {"b":"B","c":"C"} 
a = SimpleNamespace(**v) 
setattr(a,"g","G") 
a.g = "G+" 
something = a.a 
2

Nếu bạn không muốn sử dụng bất kỳ đối tượng, bạn vẫn có thể sử dụng setattr() bên trong mô-đun hiện tại của bạn:

import sys 
current_module = module = sys.modules[__name__] # i.e the "file" where your code is written 
setattr(current_module, 'variable_name', 15) # 15 is the value you assign to the var 
print(variable_name) # >>> 15, created from a string 
+0

Điều này nghe có vẻ tốt hơn tôi bằng cách sử dụng 'exec'. – fralau

+0

Tuy nhiên, điều này không hoạt động với biến '__dict__'. Tôi tự hỏi nếu có một cơ chế chung để tạo ra * bất kỳ * biến toàn cục động nào. – Alexey

+0

'globals()' có thể làm điều này –

Các vấn đề liên quan