Neden bir işlevi python sözlüğünün içinde saklıyorsunuz?


68

Ben bir piton acemiyim ve sözlükleri ve işlevleri içeren bir teknik öğrendim. Sözdizimi kolaydır ve önemsiz bir şeye benziyor, ancak piton duyumlar karıncalanıyor. Bir şey bana bunun derin ve çok pitonik bir kavram olduğunu söylüyor ve önemini tam olarak anlamadım. Birisi bu tekniğe bir isim koyabilir ve nasıl / neden işe yaradığını açıklayabilir mi?


Tekniği, bir python sözlüğünüz ve üzerinde kullanmayı düşündüğünüz bir işlevin olduğu zamandır. Değere, işlevin adı olan dikte ek bir öğe eklersiniz. Fonksiyonu çağırmaya hazır olduğunuzda , fonksiyona isme göre değil, dict elemanına bakarak aramayı dolaylı olarak düzenlersiniz.

Çalıştığım örnek, Python'u Zor Yoldan Öğren, 2nd Ed. (Bu, Udemy.com üzerinden kaydolduğunuzda kullanılabilen sürümdür ; ne yazık ki canlı ücretsiz HTML sürümü şu anda Ed 3'tür ve artık bu örneği içermemektedir).

Kelimeleri ifade etmek:

# make a dictionary of US states and major cities
cities = {'San Diego':'CA', 'New York':'NY', 'Detroit':'MI'}

# define a function to use on such a dictionary
def find_city (map, city):
    # does something, returns some value
    if city in map:
        return map[city]
    else:
        return "Not found"

# then add a final dict element that refers to the function
cities['_found'] = find_city

Sonra aşağıdaki ifadeler eşdeğerdir. Fonksiyonu doğrudan ya da değeri fonksiyon olan dict elemanına başvurarak arayabilirsiniz.

>>> find_city (cities, 'New York')
NY

>>> cities['_found'](cities, 'New York')
NY

Birisi bunun hangi dil özelliğini ve belki de “gerçek” programlamada oynayacağı yeri açıklayabilir mi? Bu oyuncak egzersizi bana sözdizimini öğretmek için yeterliydi, ama beni oraya kadar götürmedi.


13
Bu yayın neden konu dışı olsun? Harika bir algoritma ve veri yapısı kavramı sorusu!
Martijn Pieters

Diğer dillerde bunun gibi bazı şeyler gördüm (ve yaptım). Buna bir switch ifadesi olarak bakabilir, ancak O (1) arama zamanı ile geçirilebilir bir nesneye güzelce sarılmış olabilirsiniz.
KChaloux

1
İşin kendi diktininin içine dahil edilmesinde önemli ve kendine atıfta bulunan bir şey olduğunu öğrendim ... bkz.
mdeutschmtl

Yanıtlar:


83

Bir dict kullanarak, anahtarı aranabilir duruma getirelim. Anahtar, örneğinizde olduğu gibi yine de kodlanmış olmak zorunda değildir.

Genellikle bu, bir işleve bağlanmak için bir değişkenin değerini kullandığınız bir arayan gönderme biçimidir. Bir ağ işleminin size komut kodları gönderdiğini söyleyin, gönderme eşlemesi komut kodlarını kolayca çalıştırılabilir koda çevirmenizi sağlar:

def do_ping(self, arg):
    return 'Pong, {0}!'.format(arg)

def do_ls(self, arg):
    return '\n'.join(os.listdir(arg))

dispatch = {
    'ping': do_ping,
    'ls': do_ls,
}

def process_network_command(command, arg):
    send(dispatch[command](arg))

Şimdi çağırdığımız fonksiyonun tamamen değerin ne olduğuna bağlı olduğunu unutmayın command. Anahtarın da eşleşmesi gerekmez; bir dize olması gerekmiyor, anahtar olarak kullanılabilecek herhangi bir şeyi kullanabilirsiniz ve kendi uygulamanıza uyuyor.

Bir gönderme yöntemi kullanmak, eval()önceden tanımladığınız komutlarla izin verilen komutları sınırlandırması gibi diğer tekniklerden daha güvenlidir . ls)"; DROP TABLE Students; --Örneğin, hiçbir saldırgan sevk irsaliyesi masasına enjeksiyon yapamaz .


5
@Martjin - Bu durumda bu durumda 'Komuta Örüntüsünün' uygulaması olarak adlandırılamaz mı? OP'nin kavramaya çalıştığı kavram bu gibi mi görünüyor?
Doktora

3
@PhD: Evet, yaptığım örnek bir Komut Kalıbı uygulaması; dictmemuru gibi davranır (komut yöneticisi, çağıran, vs.).
Martijn Pieters,

Harika üst düzey açıklama, @Martijn, teşekkürler. Sanırım "gönderme" fikrini alıyorum.
mdeutschmtl

28

@ Martijn Pieters tekniği açıklayan iyi bir iş çıkardı, ancak sorunuzdan bir şeyi açıklığa kavuşturmak istedim.

Bilmek önemli şey olmasıdır DEĞİL sözlükte "işlevin adını" saklanması. İşlevin kendisine bir referans saklıyorsunuz. Bunu bir printişlevi kullanarak görebilirsiniz .

>>> def f():
...   print 1
... 
>>> print f
<function f at 0xb721c1b4>

ftanımladığınız işleve başvuran sadece bir değişkendir. Sözlük kullanmak, benzer şeyleri gruplandırmanıza izin verir, ancak farklı bir değişkene işlev atamaktan farklı değildir.

>>> a = f
>>> a
<function f at 0xb721c3ac>
>>> a()
1

Benzer şekilde, bir işlevi argüman olarak iletebilirsiniz.

>>> def c(func):
...   func()
... 
>>> c(f)
1

5
Birinci sınıf fonksiyondan
bahsetmek

7

Python sınıfının gerçekten sözlük için sadece bir sözdizimi şekeri olduğuna dikkat edin. Ne zaman yaparsın:

class Foo(object):
    def find_city(self, city):
        ...

Aradığın zaman

f = Foo()
f.find_city('bar')

gerçekten sadece aynıdır:

getattr(f, 'find_city')('bar')

Bu, ad çözümlemesinden sonra, aynı şekilde:

f.__class__.__dict__['find_city'](f, 'bar')

Yararlı bir teknik, kullanıcı girişinin geri aramalarla eşleştirilmesidir. Örneğin:

def cb1(...): 
    ...
funcs = {
    'cb1': cb1,
    ...
}
while True:
    input = raw_input()
    funcs[input]()

Bu alternatif olarak sınıfta yazılabilir:

class Funcs(object):
    def cb1(self, a): 
        ...
funcs = Funcs()
while True:
    input = raw_input()
    getattr(funcs, input)()

Hangi geri arama sözdizimi daha iyi olursa olsun, özel uygulamaya ve programcının zevkine göre değişir. Birincisi daha işlevsel bir tarza sahip, ikincisi daha fazla nesne yönelimli. İşlev sözlüğündeki girişleri dinamik olarak (belki de kullanıcı girişine bağlı olarak) değiştirmeniz gerekirse, birincisi daha doğal olabilir; Dinamik olarak seçilebilecek bir dizi farklı ön ayar eşlemeniz varsa, ikincisi daha doğal olabilir.


Bence bu değişebilirlik beni "pitonik" olarak düşündürdü, yüzeyde gördüğünüz şey daha derin bir şey sunmanın geleneksel bir yolu. Python alıştırmaları (ve python programcıları?) Bu şekilde dil özellikleri hakkında çok fazla konuşur gibi gözükse de, belki bu python için özel değildir.
mdeutschmtl

Başka bir düşünce, python'a sadece birbirine bitişik oturan, "dikt referansı ve argüman listesi" gibi "iki" terimin "neye benzediğini değerlendirmek için istekli olduğu bir şekilde var mı? Başka diller buna izin veriyor mu? Bu gelen cebir atılım yılı programlama eşdeğer çeşit 5 * xiçin 5x(basit benzetme affet).
mdeutschmtl

@mdeutschmtl: Python'a özgü değil, ancak birinci sınıf işlev veya işlev nesnesi olmayan diller, işlev çağrısının hemen ardından gelen bir sözlük erişiminin hiçbir zaman mümkün olamayacağı bir durum olmayabilir.
Yalan Ryan

2
@mdeutschmtl "yüzeyde gördüğünüz şey, daha derin bir şey sunmanın geleneksel bir yoludur". - Bu sözdizimsel şeker denir ve her yerde var
Izkata

6

Aklıma gelen ve atıfta bulunabileceğiniz iki teknik vardır, bunların hiçbiri Pythonic değildir, çünkü bunlar bir dilden daha geniş değildir.

1. Bilgi gizleme / enkapsülasyon ve uyum tekniği (genellikle el ele giderler, bu yüzden onları bir araya getiriyorum).

Verileri olan bir nesneye sahipsiniz ve verilerle oldukça uyumlu bir yöntem (davranış) ekliyorsunuz. Fonksiyonu değiştirmeniz, işlevselliği arttırmanız veya arayanların değiştirmesi gerekmeyecek başka değişiklikler yapmanız gerektiğinde (başka veri iletilmesi gerekmediği varsayılarak).

2. Sevk tabloları

Klasik durum değil, çünkü işlevli sadece bir giriş var. Bununla birlikte, gönderme tabloları, farklı davranışları, aranabilecek ve dinamik olarak adlandırılabilecek bir anahtarla düzenlemek için kullanılır. Bunu düşündüğünüzden emin değilsiniz çünkü işleve dinamik bir şekilde atıfta bulunmuyorsunuz, ancak hiçbiri hala etkili geç bağlama ("dolaylı" çağrı) kazanıyorsunuz.

tradeoffs

Unutulmaması gereken bir şey, yaptığınız şeyin bilinen bir anahtar ad alanıyla iyi çalışacağıdır. Bununla birlikte, verilerle işlevler arasında, bilinmeyen bir anahtar ad alanına sahip çarpışma riski vardır.


Kapsülleme, çünkü dikte (iyoner) içinde depolanan veri ve fonksiyon birbiriyle ilişkilidir ve dolayısıyla uyumu vardır. Veriler ve fonksiyonlar çok farklı iki alandandır, bu nedenle ilk bakışta örnek, birbirinden tamamen ayrı varlıkları topluyor gibi görünüyor.
ChuckCottrill

0

Oldukça genel olduğunu ve belirli durumlara adapte olmak için basit ve kolay tutulduğu için yararlı olabileceğini düşündüğüm bu çözümü gönderiyorum:

def p(what):
    print 'playing', cmpsr

def l(what):
    print 'listening', cmpsr

actions = {'Play' : p, 'Listen' : l}

act = 'Listen'
cmpsr = 'Vivaldi'

actions[act].__call__(cmpsr)

biri ayrıca her bir öğenin bir işlev nesnesi olduğu bir liste tanımlayabilir ve __call__yerleşik yöntemi kullanır. İlham ve işbirliği için herkese krediler.

"Büyük sanatçı basitleştirici", Henri Frederic Amiel

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.