পাইথনে ল্যাম্বডা এক্সপ্রেশনের ভিতরে অ্যাসাইনমেন্ট


105

আমার কাছে অবজেক্টগুলির একটি তালিকা রয়েছে এবং আমি একটি, ব্যবহার filterএবং একটি lambdaএক্সপ্রেশন বাদে খালি খালি সমস্ত বস্তু মুছে ফেলতে চাই ।

উদাহরণস্বরূপ যদি ইনপুটটি হয়:

[Object(name=""), Object(name="fake_name"), Object(name="")]

... তারপরে আউটপুটটি হওয়া উচিত:

[Object(name=""), Object(name="fake_name")]

একটি lambdaঅভিব্যক্তি একটি অ্যাসাইনমেন্ট যোগ করার উপায় আছে ? উদাহরণ স্বরূপ:

flag = True 
input = [Object(name=""), Object(name="fake_name"), Object(name="")] 
output = filter(
    (lambda o: [flag or bool(o.name), flag = flag and bool(o.name)][0]),
    input
)

1
না। তবে আপনার এটি দরকার নেই। আসলে আমি মনে করি এটি কাজ করা সত্ত্বেও এটি অর্জনের জন্য একটি সুন্দর অস্পষ্ট উপায়।

8
কেন কেবল ফিল্টারে নিয়মিত পুরানো ফাংশনটি পাস করবেন না?
ডিএফবি

5
আমি ল্যাম্বদাটি ব্যবহার করতে চেয়েছিলাম যাতে এটি সত্যিই সংক্ষিপ্ত সমাধান হতে পারে। আমার মনে আছে ওসিএএমএল-এ আমি প্রত্যাবর্তনের অভিব্যক্তির আগে মুদ্রণ বিবৃতিগুলি শৃঙ্খল করতে পেরেছিলাম, ভেবেছিলাম এটি পাইথনে প্রতিলিপি করা যেতে পারে
বিড়াল

শৃঙ্খলিত পাইপলিন বিকাশের প্রবাহে থাকা বেশ বেদনাদায়ক তখন বুঝতে হবে: "ওহ আমি প্রবাহকে আরও সুস্পষ্ট করতে একটি টেম্পর ভার তৈরি করতে চাই" বা "আমি এই মধ্যবর্তী পদক্ষেপটি লগ করতে চাই": এবং তারপরে আপনাকে লাফাতে হবে এটি করার জন্য কোনও ফাংশন তৈরি করতে অন্য কোথাও: এবং সেই ফাংশনটির নাম দিন এবং এটি ট্র্যাক রাখুন - যদিও এটি কেবল একটি জায়গায় ব্যবহার করা হয়েছে।
জাভাদবা

উত্তর:


215

:=পাইথন ৩.৮-তে যোগ করা অ্যাসাইনমেন্ট এক্সপ্রেশন অপারেটর লাম্বদা এক্সপ্রেশনগুলির ভিতরে অ্যাসাইনমেন্ট সমর্থন করে। এই অপারেটরটি সিন্থেটিক কারণে শুধুমাত্র একটি প্রথম বন্ধনীযুক্ত (...), বন্ধনীযুক্ত [...]বা ব্রেসড {...}এক্সপ্রেশনের মধ্যে উপস্থিত হতে পারে । উদাহরণস্বরূপ, আমরা নিম্নলিখিত লিখতে সক্ষম হবে:

import sys
say_hello = lambda: (
    message := "Hello world",
    sys.stdout.write(message + "\n")
)[-1]
say_hello()

পাইথন 2 এ, তালিকা বোধের পার্শ্ব প্রতিক্রিয়া হিসাবে স্থানীয় কার্য সম্পাদন করা সম্ভব হয়েছিল।

import sys
say_hello = lambda: (
    [None for message in ["Hello world"]],
    sys.stdout.write(message + "\n")
)[-1]
say_hello()

যাইহোক, আপনার উদাহরণগুলিতে flagএর মধ্যে দুটিও ব্যবহার করা সম্ভব নয় কারণ আপনার পরিবর্তনশীল কোনও বাহ্যিক স্কোপে রয়েছে, lambdaএর স্কোপ নয়। এই না হবে তা নয় lambda, এটা পাইথন 2. পাইথন সাধারণ আচরণের জন্য 3 আপনার সাথে এই কাছাকাছি পেতে দেয় এর nonlocalএর শব্দ ভিতরে defগুলি কিন্তু nonlocalভিতরে ব্যবহার করা যাবে না lambdaসে।

এখানে একটি কার্যনির্বাহ রয়েছে (নীচে দেখুন), তবে আমরা যখন বিষয়টিতে রয়েছি ...


কিছু ক্ষেত্রে আপনি এটির অভ্যন্তরে সমস্ত কিছু করতে এটি ব্যবহার করতে পারেন lambda:

(lambda: [
    ['def'
        for sys in [__import__('sys')]
        for math in [__import__('math')]

        for sub in [lambda *vals: None]
        for fun in [lambda *vals: vals[-1]]

        for echo in [lambda *vals: sub(
            sys.stdout.write(u" ".join(map(unicode, vals)) + u"\n"))]

        for Cylinder in [type('Cylinder', (object,), dict(
            __init__ = lambda self, radius, height: sub(
                setattr(self, 'radius', radius),
                setattr(self, 'height', height)),

            volume = property(lambda self: fun(
                ['def' for top_area in [math.pi * self.radius ** 2]],

                self.height * top_area))))]

        for main in [lambda: sub(
            ['loop' for factor in [1, 2, 3] if sub(
                ['def'
                    for my_radius, my_height in [[10 * factor, 20 * factor]]
                    for my_cylinder in [Cylinder(my_radius, my_height)]],

                echo(u"A cylinder with a radius of %.1fcm and a height "
                     u"of %.1fcm has a volume of %.1fcm³."
                     % (my_radius, my_height, my_cylinder.volume)))])]],

    main()])()

10.0 সেমি ব্যাসার্ধ এবং 20.0 সেমি উচ্চতা সহ একটি সিলিন্ডারের আয়তন 6283.2 সেমি³ ³
20.0 সেমি ব্যাসার্ধ এবং 40.0 সেমি উচ্চতা সহ একটি সিলিন্ডারের আয়তন 50265.5 সেমি³ ³
30.0 সেমি ব্যাসার্ধ এবং 60.0 সেমি উচ্চতা সহ একটি সিলিন্ডারের আয়তন 169646.0 সেমি³ has

দয়া করে না।


... আপনার আসল উদাহরণে ফিরে যান: আপনি flagবাহ্যিক স্কোপে ভেরিয়েবলের অ্যাসাইনমেন্টগুলি সম্পাদন করতে না পারলেও আপনি পূর্বনির্ধারিত মানটি সংশোধন করতে ফাংশন ব্যবহার করতে পারেন।

উদাহরণস্বরূপ, flagএমন কোনও বস্তু হতে পারে যার .valueব্যবহার করে আমরা সেট করেছি setattr:

flag = Object(value=True)
input = [Object(name=''), Object(name='fake_name'), Object(name='')] 
output = filter(lambda o: [
    flag.value or bool(o.name),
    setattr(flag, 'value', flag.value and bool(o.name))
][0], input)
[Object(name=''), Object(name='fake_name')]

আমরা উপরের থিমটি ফিট করতে চাইলে আমরা এর পরিবর্তে একটি তালিকা উপলব্ধি ব্যবহার করতে পারি setattr:

    [None for flag.value in [bool(o.name)]]

তবে সত্যই, গুরুতর কোডে আপনার যদি সর্বদা lambdaবাইরের অ্যাসাইনমেন্টটি করাতে চলেছে তবে আপনার পরিবর্তে নিয়মিত ফাংশন সংজ্ঞাটি ব্যবহার করা উচিত ।

flag = Object(value=True)
def not_empty_except_first(o):
    result = flag.value or bool(o.name)
    flag.value = flag.value and bool(o.name)
    return result
input = [Object(name=""), Object(name="fake_name"), Object(name="")] 
output = filter(not_empty_except_first, input)

এই উত্তরের শেষ উদাহরণটি উদাহরণের মতো একই আউটপুট তৈরি করে না, তবে উদাহরণটি আউটপুটটি ভুল বলে মনে হচ্ছে এটি আমার কাছে।
জেরেমি

সংক্ষেপে, এই নিচে boils: ব্যবহার .setattr()এবং একই রকম ( অভিধান উদাহরণস্বরূপ পাশাপাশি কী করা উচিত,) যাহাই হউক না কেন কার্মিক কোডে পার্শ্ব প্রতিক্রিয়া হ্যাক করুন, @JeremyBanks দ্বারা শীতল কোড দেখানো হয়েছিল :)
jno

নোটের জন্য থেক্স assignment operator!
জাভাদবা

37

আপনি সত্যই একটি filter/ lambdaঅভিব্যক্তিতে রাষ্ট্র বজায় রাখতে পারবেন না (গ্লোবাল নেমস্পেসের অপব্যবহার না করে)। আপনি তবে একটি reduce()অভিব্যক্তি হিসাবে চারপাশে জমা হওয়া ফলাফল ব্যবহার করে অনুরূপ কিছু অর্জন করতে পারেন :

>>> f = lambda a, b: (a.append(b) or a) if (b not in a) else a
>>> input = ["foo", u"", "bar", "", "", "x"]
>>> reduce(f, input, [])
['foo', u'', 'bar', 'x']
>>> 

আপনি অবশ্যই শর্তটি কিছুটা সাময়িকভাবে মুছে ফেলতে পারেন। এই ক্ষেত্রে এটি নকলগুলি ফিল্টার করে, তবে আপনি a.count("")কেবল খালি স্ট্রিংগুলিকে সীমাবদ্ধ করতে উদাহরণস্বরূপ ব্যবহার করতে পারেন ।

বলা বাহুল্য, আপনি এটি করতে পারেন তবে আপনার সত্যই হওয়া উচিত নয়। :)

সর্বশেষে, আপনি করতে পারেন বিশুদ্ধ পাইথন মধ্যে কিছু lambda: http://vanderwijk.info/blog/pure-lambda-calculus-python/


17

ল্যাম্বডা ব্যবহার করার দরকার নেই, যখন আপনি সমস্ত নালগুলি সরিয়ে ফেলতে পারবেন এবং ইনপুটটির আকার পরিবর্তন হলে একটি পিছনে রাখবেন:

input = [Object(name=""), Object(name="fake_name"), Object(name="")] 
output = [x for x in input if x.name]
if(len(input) != len(output)):
    output.append(Object(name=""))

1
আমি মনে করি আপনার কোডটিতে একটি ছোট ভুল আছে। দ্বিতীয় লাইন হওয়া উচিত output = [x for x in input if x.name]
হালেক্স

উপাদান ক্রম গুরুত্বপূর্ণ হতে পারে।
মায়েনিকি

15

=কোনও lambdaঅভিব্যক্তির ভিতরে সাধারণ অ্যাসাইনমেন্ট ( ) সম্ভব নয় , যদিও setattrএবং বন্ধুদের সাথে বিভিন্ন কৌশল চালানো সম্ভব ।

তবে আপনার সমস্যা সমাধান করা আসলে বেশ সহজ:

input = [Object(name=""), Object(name="fake_name"), Object(name="")]
output = filter(
    lambda o, _seen=set():
        not (not o and o in _seen or _seen.add(o)),
    input
    )

যা আপনাকে দেবে

[Object(Object(name=''), name='fake_name')]

আপনি দেখতে পাচ্ছেন, এটি শেষের পরিবর্তে প্রথম ফাঁকা দৃষ্টান্তটি রাখছে। পরিবর্তে যদি আপনার শেষের প্রয়োজন হয় তবে তালিকাটি ভিতরে গিয়ে filterউল্টা করুন এবং তালিকাটি বাইরে এসে উল্টো করুন filter:

output = filter(
    lambda o, _seen=set():
        not (not o and o in _seen or _seen.add(o)),
    input[::-1]
    )[::-1]

যা আপনাকে দেবে

[Object(name='fake_name'), Object(name='')]

একটি বিষয় সচেতন হতে হবে: স্বেচ্ছাসেবী বস্তুগুলির সাথে এটি কাজ করার জন্য, সেই জিনিসগুলি অবশ্যই যথাযথভাবে প্রয়োগ করা উচিত __eq__এবং এখানে__hash__ বর্ণিত হিসাবে ।


7

আপডেট :

[o for d in [{}] for o in lst if o.name != "" or d.setdefault("", o) == o]

বা ব্যবহার filterএবং lambda:

flag = {}
filter(lambda o: bool(o.name) or flag.setdefault("", o) == o, lst)

পূর্ববর্তী উত্তর

ঠিক আছে, আপনি ফিল্টার এবং ল্যাম্বডা ব্যবহার আটকে আছেন?

মনে হচ্ছে এটি অভিধান বোঝার সাথে আরও ভাল পরিবেশন করা হবে,

{o.name : o for o in input}.values()

আমি মনে করি যে পাইথন একটি ল্যাম্বডায় অ্যাসাইনমেন্টের অনুমতি দেয় না তার অনুরূপ কেন এটি কোনও বোঝার জন্য অ্যাসাইনমেন্টের অনুমতি দেয় না এবং এই বিষয়গুলির Cপাশ দিয়ে মূল্যায়ন করা হয় এবং এটি আমাদের একটি ফলস্বরূপ প্রদান করতে পারে তার সাথে কিছু করার দরকার পড়েছে গতি বৃদ্ধি। গুইডোর একটি রচনা পড়ে কমপক্ষে এটি আমার ছাপ ।

আমার ধারণা এটি পাইথনের যে কোনও একটি কাজ করার এক সঠিক উপায় থাকার দর্শনের বিরুদ্ধে যাবে ।


সুতরাং এটি পুরোপুরি সঠিক নয়। এটি অর্ডার সংরক্ষণ করবে না, এটি খালি-স্ট্রিংযুক্ত বস্তুগুলির সদৃশও সংরক্ষণ করবে না।
JPvdMerwe

7

টিএল; ডিআর: ফাংশনাল আইডিয়ামগুলি ব্যবহার করার সময় ফাংশনাল কোড লিখাই ভাল

যেমনটি অনেকে উল্লেখ করেছেন, পাইথনে ল্যাম্বডাস অ্যাসাইনমেন্টের অনুমতি নেই। সাধারণভাবে ফাংশনাল আইডিয়ামগুলি ব্যবহার করার সময় আপনার কার্যক্ষম পদ্ধতিতে চিন্তাভাবনা বন্ধ করে দেওয়া হয় যার অর্থ যেখানেই সম্ভব কোনও পার্শ্ব প্রতিক্রিয়া এবং কোনও অ্যাসাইনমেন্ট।

এখানে কার্যকরী সমাধান যা একটি ল্যাম্বডা ব্যবহার করে। আমি ল্যাম্বডাকে fnস্পষ্টতার জন্য বরাদ্দ করেছি (এবং কারণ এটি কিছুটা দীর্ঘ-ইশ পেয়েছে)।

from operator import add
from itertools import ifilter, ifilterfalse
fn = lambda l, pred: add(list(ifilter(pred, iter(l))), [ifilterfalse(pred, iter(l)).next()])
objs = [Object(name=""), Object(name="fake_name"), Object(name="")]
fn(objs, lambda o: o.name != '')

আপনি কিছু কাছাকাছি জিনিস পরিবর্তন করে তালিকার চেয়ে পুনরাবৃত্তকারীদের সাথেও এই চুক্তি করতে পারেন। আপনার কিছু আলাদা আমদানিও রয়েছে।

from itertools import chain, islice, ifilter, ifilterfalse
fn = lambda l, pred: chain(ifilter(pred, iter(l)), islice(ifilterfalse(pred, iter(l)), 1))

বিবৃতিগুলির দৈর্ঘ্য হ্রাস করতে আপনি সর্বদা কোডটি পুনরায় সাজিয়ে নিতে পারেন।


6

পরিবর্তে যদি flag = Trueআমরা এর পরিবর্তে আমদানি করতে পারি তবে আমি মনে করি এটি মানদণ্ডগুলি পূরণ করে:

>>> from itertools import count
>>> a = ['hello', '', 'world', '', '', '', 'bob']
>>> filter(lambda L, j=count(): L or not next(j), a)
['hello', '', 'world', 'bob']

অথবা ফিল্টারটি আরও ভালভাবে লিখিত হয়েছে:

>>> filter(lambda L, blank_count=count(1): L or next(blank_count) == 1, a)

বা, কোনও আমদানি ছাড়াই কেবল একটি সাধারণ বুলিয়ান জন্য:

filter(lambda L, use_blank=iter([True]): L or next(use_blank, False), a)

6

পুনরাবৃত্তির সময় রাষ্ট্র ট্র্যাক করার অজগর উপায়টি জেনারেটর সহ। আইটিএইচটিও বুঝতে ইটারটোলস উপায় বেশ শক্ত এবং এটি করার জন্য ল্যাম্বডাস হ্যাক করার চেষ্টাটি সহজ বোকা। আমি চেষ্টা করব:

def keep_last_empty(input):
    last = None
    for item in iter(input):
        if item.name: yield item
        else: last = item
    if last is not None: yield last

output = list(keep_last_empty(input))

সামগ্রিকভাবে, পঠনযোগ্যতা প্রতিবার সংযোগকে ট্রাম্প করে।


4

না, আপনি নিজের সংজ্ঞা হিসাবে ল্যাম্বডায় একটি অ্যাসাইনমেন্ট রাখতে পারবেন না। আপনি যদি ক্রিয়ামূলক প্রোগ্রামিং ব্যবহার করে কাজ করেন, তবে আপনাকে অবশ্যই ধরে নিতে হবে যে আপনার মানগুলি পরিবর্তনযোগ্য নয়।

একটি সমাধান নিম্নলিখিত কোড হবে:

output = lambda l, name: [] if l==[] \
             else [ l[ 0 ] ] + output( l[1:], name ) if l[ 0 ].name == name \
             else output( l[1:], name ) if l[ 0 ].name == "" \
             else [ l[ 0 ] ] + output( l[1:], name )

4

কলগুলির মধ্যে রাষ্ট্রের কথা মনে রাখার জন্য যদি আপনার ল্যাম্বডা দরকার হয় তবে আমি স্থানীয় নেমস্পেসে ঘোষিত কোনও ফাংশন বা একটি ওভারলোডেড সহ একটি শ্রেণীর সুপারিশ করব __call__। এখন আপনি যা করার চেষ্টা করছেন তার বিরুদ্ধে আমার সমস্ত সতর্কতাগুলি এড়িয়ে গেছে, আমরা আপনার প্রশ্নের সঠিক উত্তর পেতে পারি can

যদি কলগুলির মধ্যে কিছুটা মেমরি রাখার জন্য আপনার ল্যাম্বডাটি সত্যই দরকার হয় তবে আপনি এটির সংজ্ঞা দিতে পারেন:

f = lambda o, ns = {"flag":True}: [ns["flag"] or o.name, ns.__setitem__("flag", ns["flag"] and o.name)][0]

তারপরে আপনার কেবল পাস fকরতে হবে filter()। আপনার যদি সত্যিই প্রয়োজন হয় তবে আপনি flagনিম্নলিখিতগুলির সাথে মানটি ফিরে পেতে পারেন :

f.__defaults__[0]["flag"]

বিকল্পভাবে, আপনি ফলাফল পরিবর্তন করে বৈশ্বিক নেমস্পেস পরিবর্তন করতে পারেন globals()। দুর্ভাগ্যক্রমে, আপনি স্থানীয় নেমস্পেসকে একইভাবে সংশোধন করতে পারবেন না ফলাফলের পরিবর্তন যেমন locals()স্থানীয় নামস্থান প্রভাবিত করে না।


অথবা শুধু মূল পাতার মর্মর ব্যবহার করুন: (let ((var 42)) (lambda () (setf var 43)))
কাজ

4

আপনি সিউডো মাল্টি-স্টেটমেন্ট ল্যাম্বডা ব্যবহার করতে একটি বাঁধন ফাংশন ব্যবহার করতে পারেন। তারপরে আপনি অ্যাসাইনমেন্ট সক্ষম করতে ফ্ল্যাগের জন্য একটি মোড়কের ক্লাস ব্যবহার করতে পারেন।

bind = lambda x, f=(lambda y: y): f(x)

class Flag(object):
    def __init__(self, value):
        self.value = value

    def set(self, value):
        self.value = value
        return value

input = [Object(name=""), Object(name="fake_name"), Object(name="")]
flag = Flag(True)
output = filter(
            lambda o: (
                bind(flag.value, lambda orig_flag_value:
                bind(flag.set(flag.value and bool(o.name)), lambda _:
                bind(orig_flag_value or bool(o.name))))),
            input)

0

অগোছালো কাজের মতো, তবে ল্যাম্বডাসে অ্যাসাইনমেন্ট কোনওভাবেই অবৈধ, সুতরাং এটি সত্যিই গুরুত্বপূর্ণ নয়। ল্যাম্বদার exec()ভিতরে থেকে অ্যাসাইনমেন্ট চালাতে আপনি বিল্টিন ফাংশনটি ব্যবহার করতে পারেন , যেমন এই উদাহরণ:

>>> val
Traceback (most recent call last):
  File "<pyshell#31>", line 1, in <module>
    val
NameError: name 'val' is not defined
>>> d = lambda: exec('val=True', globals())
>>> d()
>>> val
True

-2

প্রথমত, আপনাকে আপনার কাজের জন্য স্থানীয় অ্যাসিগমেন্ট ব্যবহার করতে হবে না, কেবল উপরের উত্তরটি দেখুন

দ্বিতীয়ত, স্থানীয় () এবং গ্লোবালগুলি () ব্যবহার করে ভেরিয়েবল টেবিলটি পাওয়া যায় এবং তারপরে মানটি পরিবর্তন করা যায় simple

এই নমুনা কোডটি পরীক্ষা করুন:

print [locals().__setitem__('x', 'Hillo :]'), x][-1]

আপনি যদি আপনার পরিবেশে একটি বৈশ্বিক পরিবর্তনশীল যুক্ত করতে চান তবে স্থানীয়দের () কে গ্লোবাল () দিয়ে প্রতিস্থাপন করার চেষ্টা করুন

পাইথনের তালিকার সংকলনটি দুর্দান্ত তবে ত্রিশূল প্রকল্পের বেশিরভাগ এটি গ্রহণ করে না (ফ্লাস্কের মতো: [)

আশা করি এটি সাহায্য করতে পারে


2
আপনি ব্যবহার করতে পারবেন না locals(), এটি ডকুমেন্টেশনে স্পষ্টতই বলেছে যে এটি পরিবর্তন করা আসলে স্থানীয় সুযোগ পরিবর্তন করে না (বা কমপক্ষে এটি সবসময় হয় না)। globals()অন্যদিকে প্রত্যাশার মতো কাজ করে।
JPvdMerwe 31'13

@JPvdMerwe কেবল এটি চেষ্টা করে দেখুন, দস্তাবেজটি অন্ধভাবে অনুসরণ করবেন না। এবং ল্যাম্বডায় অ্যাসাইনমেন্টটি ইতিমধ্যে নিয়ম ভঙ্গ করছে
jyf1987

3
দুর্ভাগ্যক্রমে এটি কেবল বিশ্বব্যাপী নেমস্পেসে কাজ করে, সেক্ষেত্রে আপনার সত্যিকারের ব্যবহার করা উচিত globals()পেস্টবিন.com/5বিজেজ 1 এমআর 4 (2.6 এবং 3.2 উভয় ক্ষেত্রেই পরীক্ষিত) এটি প্রমাণ করে।
JPvdMerwe
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.