Làm thế nào để tôi tạo ra một số lượng biến?


364

Làm cách nào để thực hiện các biến biến trong Python?

Dưới đây là một mục thủ công chi tiết: Ví dụ: Biến biến

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


39
đó là khía cạnh duy trì và gỡ lỗi gây ra nỗi kinh hoàng. Hãy tưởng tượng bạn đang cố gắng tìm ra biến 'foo' thay đổi khi không có chỗ 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ể đến nơi hạnh phúc của mình ngay bây giờ.
glenn jackman

4
Một cạm bẫy nữa chưa được đề cập cho đến nay là nếu một biến được tạo động như vậy có cùng tên với một biến được sử dụng trong logic của bạn. Về cơ bản, bạn mở phần mềm của mình làm con tin cho đầu vào mà nó được cung cấp.
Holdenweb

Tất cả các phản hồi ở đây giả sử bạn có quyền truy cập vào các biến cơ sở mà bạn muốn truy cập động theo tên, điều này không phải lúc nào cũng đúng. Tôi nghĩ cách tiếp cận chung nhất để tái tạo hành vi ví dụ trong PHP là sử dụng eval () như thế này: var_name = 'foo'; thanh = 5; đầu ra = eval (var_name)
Luis Vazquez

1
Bạn có thể sửa đổi các biến toàn cục và cục bộ của mình bằng cách truy cập các từ điển cơ bản cho chúng; đó là một ý tưởng khủng khiếp từ góc độ bảo trì ... nhưng nó có thể được thực hiện thông qua globalals (). update ()localals (). update () (hoặc bằng cách lưu tham chiếu chính tả từ một trong hai từ đó và sử dụng nó như bất kỳ từ điển nào khác ). KHÔNG ĐƯỢC KHUYẾN NGHỊ ... nhưng bạn nên biết rằng điều đó là có thể.
Jim Dennis

(Tôi đang thêm nhận xét này vì một câu hỏi tương tự, cụ thể hơn là yêu cầu loại thao tác này đã được đóng bằng một con trỏ cho câu hỏi "đủ gần" này. Tôi muốn những người theo dõi câu hỏi đóng để có câu trả lời của họ).
Jim Dennis

Câu trả lời:


297

Bạn có thể sử dụng từ điển để thực hiện điều này. Từ điển là cửa hàng của các 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 biến mà 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ĩ làm một cái gì đó như

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

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

lst = ['foo', 'bar', 'baz']
print(lst[1])           # prints bar, because indices start at 0
lst.append('potatoes')  # lst 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, cắt , appendvà các hoạt động khác mà có thể yêu cầu quản lý chủ chốt vụng về với một dict.


88

Sử dụng hàm dựng sẵn getattrđể lấy một 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'

70

Nó không phải là một ý tưởng tốt. 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 một biến trong phạm vi cục bộ bạn có thể sử dụng locals(), nhưng bạn không thể gán giá trị cho dict được trả về.

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


localals (). update ({'new_local_var': 'một số giá trị cục bộ'}) hoạt động tốt với tôi trong Python 3.7.6; Vì vậy, tôi không chắc ý của bạn là gì khi bạn nói rằng bạn không thể gán giá trị thông qua nó.
Jim Dennis

Cho x = "foo"locals()["x"] = "bar"sử dụng print xcung cấp đầu ra barcho Jython 2.5.2. Điều này đã được thử nghiệm với Tập lệnh tự động hóa theo yêu cầu trong maximo .
Thầy giảng

37

Bất cứ khi nào bạn muốn sử dụng các biến số, có lẽ tốt hơn là 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 hiện có trước đây (đó là khía cạnh bảo mật) và bạn có thể có các "không gian tên" khác nhau.


34

Các lập trình viên 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)
...

Bộ mã hóa sau đó được để lại một đống các biến được đặt tên, với nỗ lực mã hóa là O ( m * n ), trong đó m là số lượng biến được đặt tên và n là số lần mà nhóm biến cần truy cập (bao gồm cả việc tạo ). Người mới bắt đầu sắc sảo hơn nhận thấy 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 một vòng lặp. Tuy nhiên, họ bị mắc kẹt 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 thấy rằng điều này không hoạt động.

Nếu chương trình yêu cầu "tên" tùy ý, thì 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 nhiều biến và bạn không ngại đề cập đến chúng bằng một chuỗi số nguyên, có lẽ 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 là đồng nhất, chẳng hạn như đọc nhiệt độ hàng ngày, điểm kiểm tra hàng tuần hoặc một lưới các vật dụng đồ 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))

Điều này listcũng có thể được tạo ra trong một dòng với 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 quần thể list, với phần tử đầu tiên được truy cập my_calculator.buttons[0], phần tiếp theo my_calculator.buttons[1], v.v. Tên biến "cơ sở" trở thành tên của listvà định danh 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ị kèm theo. Nếu bạn chỉ cần một "túi" đồ vật, đâ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 listchuỗi cho các đối tượng tương tự, một settúi đối tượng được sắp xếp tùy ý hoặc dictmột túi tên có giá trị liên quan.


12

Thay vì từ điển, bạn cũng có thể sử dụng namedtupletừ 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)

10

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

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

Điều này nghe có vẻ tốt với tôi hơn là sử dụng 'exec'.
fralau

Điều này không làm việc với __dict__biến tuy nhiên. Tôi tự hỏi nếu có một cơ chế chung để tạo ra bất kỳ biến toàn cầu nào một cách linh hoạt.
Alexey

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

9

Các SimpleNamespacelớp có thể được sử dụng để tạo các thuộc tính mới với setattr, hoặc phân lớp SimpleNamespacevà tạo chức năng của riêng 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(**variables)
setattr(a,"g","G")
a.g = "G+"
something = a.a

6

Tôi đang trả lời câu hỏi: Làm thế nào để lấy giá trị của biến được đặt tên trong chuỗi? được đóng lại như một bản sao với một liên kết đến câu hỏi này.

Nếu các biến trong câu hỏi là một phần của một đối tượng (một phần của một lớp chẳng hạn) sau đó một số chức năng hữu ích để đạt được chính xác điều đó 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")

"init_variable"

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

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


5

Sử dụng globals()

Ví dụ, bạn thực sự có thể gán biến cho phạm vi toàn cầu một cách linh hoạt, nếu bạn muốn 10 biến có thể được truy cập trên phạm vi toàn cầu i_1, i_2... i_10:

for i in range(10):
    globals()['i_{}'.format(i)] = 'a'

Điều này sẽ gán 'a' cho tất cả 10 biến này, tất nhiên bạn cũng có thể thay đổi giá trị một cách linh hoạt. Tất cả các biến này có thể được truy cập ngay bây giờ giống như các biến được khai báo toàn cầu khác:

>>> i_5
'a'

4

Bạn phải sử dụng globals()phương thức được xây dựng để đạ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

3

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

  • bạn sẽ tự chịu trách nhiệm cho từ điển này, bao gồm cả bộ sưu tập rác (của các biến in-dict), v.v.
  • không có địa phương hoặc tính toàn cầu cho các biến số, 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 thực hiện thủ công
  • tuy nhiên, bạn linh hoạt hơn nhiều, vd
    • bạn có thể quyết định ghi đè lên các biến hiện có hoặc ...
    • ... chọn thực hiện các biến const
    • để đưa ra một ngoại lệ về ghi đè cho các loại khác nhau
    • Vân vân.

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

Bạn sẽ sử dụng lớp 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 chỉ muốn cho phép ghi đè các biến có cùng loại:

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

Thoạt nhìn, hàng nhập khẩu camelised dài khiến tôi nghĩ đây là Java.
markroxor

3

Tôi đã thử cả trong python 3.7.3, bạn có thể sử dụng cả globalals () hoặc vars ()

>>> food #Error
>>> milkshake #Error
>>> food="bread"
>>> drink="milkshake"
>>> globals()[food] = "strawberry flavor"
>>> vars()[drink] = "chocolate flavor"
>>> bread
'strawberry flavor'
>>> milkshake
'chocolate flavor'
>>> globals()[drink]
'chocolate flavor'
>>> vars()[food]
'strawberry flavor'


Tham khảo:
https://www.daniweb.com/programming/software-development/threads/111526/setting-a-opes-as-a-variable-name#post536136


0

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

Đoạn mã sau định nghĩa lớp Biến, thêm các biến (trong trường hợp này là thuộc tính) vào thể hiện của nó trong quá trình xây dựng. Tên biến được lấy từ một danh sách cụ thể (ví dụ, có thể được tạo 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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.