ฉันจะสร้างจำนวนตัวแปรได้อย่างไร


364

ฉันจะทำตัวแปรให้สำเร็จใน Python ได้อย่างไร?

นี่คือรายการคู่มือที่ทำขึ้นเองตัวอย่างเช่น: ตัวแปรตัวแปร

ฉันเคยได้ยินว่านี่เป็นความคิดที่ไม่ดีโดยทั่วไปและเป็นช่องโหว่ใน Python มันเป็นเรื่องจริงเหรอ?


39
มันคือการบำรุงรักษาและแก้ไขจุดบกพร่องที่ทำให้สยองขวัญ ลองนึกภาพว่าการค้นหาตัวแปร 'foo' เปลี่ยนแปลงไปเมื่อไม่มีที่ในรหัสของคุณซึ่งคุณเปลี่ยน 'foo' จริง ลองนึกภาพเพิ่มเติมว่าเป็นรหัสของคนอื่นที่คุณต้องดูแล ... ตกลงคุณสามารถไปยังสถานที่ที่มีความสุขได้แล้ว
เกล็นแจ็คแมน

4
ข้อผิดพลาดเพิ่มเติมที่ยังไม่ได้กล่าวถึงคือถ้าตัวแปรที่สร้างขึ้นแบบไดนามิกนั้นมีชื่อเหมือนกับตัวแปรที่ใช้ในตรรกะของคุณ คุณเปิดซอฟต์แวร์เป็นตัวประกันกับอินพุตที่ให้ไว้
holdenweb

การตอบกลับทั้งหมดที่นี่ถือว่าคุณสามารถเข้าถึงตัวแปรพื้นฐานที่คุณต้องการเข้าถึงแบบไดนามิกตามชื่อซึ่งไม่ได้เป็นทุกกรณี ฉันคิดว่าวิธีทั่วไปที่สุดในการทำซ้ำตัวอย่างพฤติกรรมใน PHP คือการใช้ eval () เช่นนี้: var_name = 'foo'; บาร์ = 5; output = eval (var_name)
Luis Vazquez

1
คุณสามารถปรับเปลี่ยนตัวแปรโกลบอลและโลคัลได้โดยการเข้าถึงพจนานุกรมพื้นฐานสำหรับพวกเขา มันเป็นความคิดที่น่ากลัวจากมุมมองการบำรุงรักษา ... แต่สามารถทำได้ผ่านทางglobals (). update ()และlocals (). update () (หรือโดยการบันทึกการอ้างอิง dict จากทั้งคู่และใช้มันเหมือนพจนานุกรมอื่น ๆ ) ไม่แนะนำ ... แต่คุณควรรู้ว่าเป็นไปได้
Jim Dennis

(ฉันกำลังเพิ่มความคิดเห็นนี้เพราะคำถามที่คล้ายกันโดยเฉพาะอย่างยิ่งการร้องขอการจัดการแบบนี้ถูกปิดด้วยตัวชี้ไปยังคำถาม "ใกล้พอ" ฉันต้องการให้ผู้ที่ติดตามคำถามที่ปิดเพื่อรับคำตอบ)
จิมเดนนิส

คำตอบ:


297

คุณสามารถใช้พจนานุกรมเพื่อทำสิ่งนี้ให้สำเร็จ พจนานุกรมคือที่จัดเก็บคีย์และค่า

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

คุณสามารถใช้ชื่อคีย์ตัวแปรเพื่อให้ได้ผลของตัวแปรตัวแปรโดยไม่มีความเสี่ยงด้านความปลอดภัย

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

สำหรับกรณีที่คุณกำลังคิดที่จะทำอะไรบางอย่างเช่น

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

รายการอาจเหมาะสมกว่า dict รายการหมายถึงลำดับของวัตถุที่มีดัชนีเป็นจำนวนเต็ม:

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

สำหรับลำดับการสั่งซื้อรายการมีความสะดวกมากขึ้นกว่า dicts ด้วยปุ่มจำนวนเต็มเพราะรายชื่อสนับสนุนในการสั่งซื้อซ้ำดัชนีหั่น , appendและการดำเนินงานอื่น ๆ ที่จะต้องมีการจัดการที่สำคัญที่น่าอึดอัดใจกับ Dict


88

ใช้getattrฟังก์ชั่นในตัวเพื่อรับแอททริบิวบนวัตถุตามชื่อ แก้ไขชื่อตามต้องการ

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

70

มันไม่ใช่ความคิดที่ดี globals()หากคุณมีการเข้าถึงตัวแปรทั่วโลกที่คุณสามารถใช้

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

หากคุณต้องการเข้าถึงตัวแปรในขอบเขตท้องถิ่นคุณสามารถใช้locals()แต่คุณไม่สามารถกำหนดค่าให้กับ dict ที่ส่งคืน

ทางออกที่ดีกว่าคือการใช้getattrหรือเก็บตัวแปรของคุณในพจนานุกรมจากนั้นเข้าถึงพวกเขาตามชื่อ


locals (). update ({'new_local_var': 'ค่าท้องถิ่นบางค่า'}) ใช้งานได้ดีสำหรับฉันใน Python 3.7.6; ดังนั้นฉันไม่แน่ใจว่าคุณหมายถึงอะไรเมื่อคุณพูดว่าคุณไม่สามารถกำหนดค่าผ่านมันได้
จิมเดนนิส

ให้x = "foo"และการlocals()["x"] = "bar"ใช้งานprint xจะให้เอาต์พุตbarสำหรับ Jython 2.5.2 นี้ได้รับการทดสอบกับสคริปต์อัตโนมัติความต้องการในmaximo
นักเทศน์

37

เมื่อใดก็ตามที่คุณต้องการใช้ตัวแปรตัวแปรคุณควรใช้พจนานุกรม ดังนั้นแทนที่จะเขียน

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

ที่คุณเขียน

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

วิธีนี้คุณจะไม่เขียนทับตัวแปรที่มีอยู่ก่อนหน้านี้โดยไม่ตั้งใจ (ซึ่งเป็นด้านความปลอดภัย) และคุณสามารถมี "namespaces" ที่แตกต่างกัน


34

โค้ดใหม่บางครั้งเขียนโค้ดดังนี้:

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)
...

จากนั้นโค้ดจะถูกทิ้งไว้กับกองของตัวแปรที่มีชื่อพร้อมความพยายามเข้ารหัสของ O ( m * n ) โดยที่mคือจำนวนตัวแปรที่มีชื่อและnคือจำนวนครั้งที่กลุ่มตัวแปรต้องเข้าถึง (รวมถึงการสร้าง) ) เริ่มต้นที่ชาญฉลาดมากขึ้นสังเกตว่าความแตกต่างเพียงอย่างเดียวในแต่ละบรรทัดเหล่านั้นเป็นจำนวนที่เปลี่ยนแปลงตามกฎและตัดสินใจที่จะใช้วง อย่างไรก็ตามพวกเขาติดอยู่กับวิธีสร้างชื่อตัวแปรแบบไดนามิกและอาจลองดังนี้:

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

ในไม่ช้าพวกเขาก็พบว่าสิ่งนี้ไม่ได้ผล

หากโปรแกรมต้องการชื่อ "ตัวแปร" โดยพลการพจนานุกรมเป็นตัวเลือกที่ดีที่สุดตามที่อธิบายไว้ในคำตอบอื่น ๆ อย่างไรก็ตามหากคุณเพียงแค่พยายามสร้างตัวแปรจำนวนมากและคุณไม่รังเกียจที่จะอ้างถึงพวกเขาด้วยลำดับจำนวนเต็มคุณอาจกำลังมองหาlistแต่ถ้าคุณเพียงแค่พยายามที่จะสร้างตัวแปรจำนวนมากและคุณไม่ทราบว่าหมายถึงพวกเขาด้วยลำดับของจำนวนเต็มคุณอาจมองหานี่เป็นเรื่องจริงโดยเฉพาะอย่างยิ่งหากข้อมูลของคุณเป็นเนื้อเดียวกันเช่นการอ่านอุณหภูมิรายวันคะแนนการตอบคำถามรายสัปดาห์หรือตารางวิดเจ็ตกราฟิก

สามารถประกอบได้ดังนี้:

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

สิ่งนี้listสามารถสร้างในหนึ่งบรรทัดด้วยความเข้าใจ:

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

ผลลัพธ์ในกรณีใดกรณีหนึ่งจะถูกเติมlistด้วยองค์ประกอบแรกที่เข้าถึงด้วยmy_calculator.buttons[0]ถัดไปด้วยmy_calculator.buttons[1]และอื่น ๆ ชื่อตัวแปร "base" จะกลายเป็นชื่อของlistและตัวระบุที่แตกต่างกันจะถูกใช้เพื่อเข้าถึง

สุดท้ายอย่าลืมโครงสร้างข้อมูลอื่น ๆ เช่นset- สิ่งนี้คล้ายกับพจนานุกรมยกเว้นว่า "ชื่อ" แต่ละรายการไม่มีค่าติดอยู่ หากคุณต้องการเพียงแค่ "กระเป๋า" ของวัตถุนี่อาจเป็นทางเลือกที่ยอดเยี่ยม แทนที่จะเป็นแบบนี้:

keyword_1 = 'apple'
keyword_2 = 'banana'

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

คุณจะมีสิ่งนี้:

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

ใช้listสำหรับลำดับของวัตถุที่คล้ายกัน, setสำหรับถุงวัตถุที่สั่งโดยพลการหรือdictสำหรับถุงชื่อที่มีค่าที่เกี่ยวข้อง


12

แทนที่จะเป็นพจนานุกรมคุณสามารถใช้namedtupleจากโมดูลคอลเล็กชันซึ่งทำให้เข้าถึงได้ง่ายขึ้น

ตัวอย่างเช่น:

# 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

หากคุณไม่ต้องการใช้วัตถุใด ๆ คุณยังสามารถใช้setattr()ภายในโมดูลปัจจุบันของคุณได้:

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

อันนี้ฟังดูดีกว่าฉันมากกว่าการใช้ 'exec'
fralau

สิ่งนี้ไม่ทำงานกับ__dict__ตัวแปรอย่างไรก็ตาม ฉันสงสัยว่ามีกลไกทั่วไปเพื่อสร้างใด ๆแบบไดนามิกตัวแปรทั่วโลก
Alexey

globals()สามารถทำได้
Guillaume Lebreton

9

SimpleNamespaceระดับสามารถนำมาใช้ในการสร้างคุณลักษณะใหม่ที่มีsetattrหรือซับคลาสSimpleNamespaceและสร้างฟังก์ชั่นของคุณเองเพื่อเพิ่มชื่อแอตทริบิวต์ใหม่ (ตัวแปร)

from types import SimpleNamespace

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

6

ฉันกำลังตอบคำถาม: วิธีรับค่าของตัวแปรตามชื่อในสตริง? ซึ่งปิดเหมือนซ้ำพร้อมลิงค์ไปยังคำถามนี้

หากตัวแปรในคำถามที่เป็นส่วนหนึ่งของวัตถุ (ส่วนหนึ่งของชั้นเช่น) แล้วบางฟังก์ชันที่มีประโยชน์เพื่อให้บรรลุตรงที่มีhasattr, getattrและsetattrและ

ตัวอย่างเช่นคุณสามารถมี:

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)

จากนั้นคุณสามารถทำได้:

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

"initial_variable"

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

"ไม่ใช่จริงเริ่มต้น"


5

ใช้ globals()

คุณสามารถกำหนดตัวแปรจริงขอบเขตทั่วโลกแบบไดนามิกตัวอย่างเช่นถ้าคุณต้องการ 10 ตัวแปรที่สามารถเข้าถึงได้ในขอบเขตทั่วโลกi_1, i_2... i_10:

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

สิ่งนี้จะกำหนด 'a' ให้กับตัวแปรทั้งหมด 10 ตัวนี้แน่นอนว่าคุณสามารถเปลี่ยนค่าแบบไดนามิกได้เช่นกัน ตัวแปรทั้งหมดเหล่านี้สามารถเข้าถึงได้ในขณะนี้เหมือนกับตัวแปรประกาศทั่วโลกอื่น ๆ :

>>> i_5
'a'

4

คุณต้องใช้globals()วิธีในตัว เพื่อให้บรรลุพฤติกรรมดังกล่าว:

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

ฉันทามติคือการใช้พจนานุกรมสำหรับสิ่งนี้ - ดูคำตอบอื่น ๆ นี่เป็นความคิดที่ดีสำหรับกรณีส่วนใหญ่อย่างไรก็ตามมีหลายด้านที่เกิดขึ้นจากสิ่งนี้:

  • คุณจะต้องรับผิดชอบพจนานุกรมนี้รวมถึงการเก็บขยะ (ของตัวแปรในคำสั่ง) เป็นต้น
  • ไม่มีท้องที่หรือกลมสำหรับตัวแปรตัวแปรมันขึ้นอยู่กับกลมของพจนานุกรม
  • หากคุณต้องการเปลี่ยนชื่อตัวแปรคุณจะต้องเปลี่ยนชื่อด้วยตนเอง
  • อย่างไรก็ตามคุณมีความยืดหยุ่นมากกว่าเช่น
    • คุณสามารถตัดสินใจที่จะเขียนทับตัวแปรที่มีอยู่หรือ ...
    • ... เลือกที่จะใช้ตัวแปร const
    • เพื่อเพิ่มข้อยกเว้นในการเขียนทับสำหรับประเภทที่แตกต่างกัน
    • เป็นต้น

ที่กล่าวว่าฉันได้ใช้ตัวจัดการตัวแปรตัวแปร - ระดับซึ่งให้แนวคิดบางอย่างข้างต้น มันใช้งานได้กับงูหลาม 2 และ 3

คุณจะใช้คลาสเช่นนี้:

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'])

หากคุณต้องการอนุญาตให้เขียนทับตัวแปรที่มีประเภทเดียวกันเท่านั้น:

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

เมื่อดูอย่างรวดเร็วครั้งแรกการนำเข้าที่มี camelised นานทำให้ฉันคิดว่านี่เป็น Java
markroxor

3

ฉันได้ลองทั้งสองใน python 3.7.3 คุณสามารถใช้ globals () หรือ 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'


การอ้างอิง:
https://www.daniweb.com/programming/software-development/threads/111526/setting-a-string-as-a-variable-name#post548936


0

ตัวแปรชุดใดก็ได้ที่สามารถห่อหุ้มในชั้นเรียนได้ ตัวแปร "Variable" อาจถูกเพิ่มไปยังอินสแตนซ์ของคลาสในระหว่างรันไทม์โดยการเข้าถึงพจนานุกรมในตัวโดยตรงผ่านแอตทริบิวต์ __dict__

รหัสต่อไปนี้กำหนดคลาสตัวแปรซึ่งจะเพิ่มตัวแปร (ในแอ็ตทริบิวต์เคสนี้) ให้กับอินสแตนซ์ของมันในระหว่างการสร้าง ชื่อตัวแปรถูกนำมาจากรายการที่ระบุ (ซึ่งอาจสร้างด้วยรหัสโปรแกรม):

# 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.