2011-07-22 7 views
5

Cụ thể hơn, tôi muốn có thể hỗ trợ lambda: <some_or_other_setter>, nhưng tôi muốn giữ mã rõ ràng và ngắn gọn. Tôi phải xác nhận giá trị, vì vậy tôi cần một setter của một số loại. Tôi cần phải sử dụng lambda vì tôi cần phải chuyển callbacks cho các sự kiện Tkinter. Tôi cũng cần có khả năng sửa đổi giá trị của thuộc tính bên ngoài một ràng buộc.Điều gì sẽ là cách nhiệt tình nhất để tạo ra một thuộc tính có thể được sử dụng trong một lambda?

Trong các ví dụ sau đây, giả sử rằng tiện ích nút có tên là spam_button đã được khai báo trên toàn cầu. Ngoài ra giả sử rằng lớp học Eggs sẽ có ít nhất 10 thuộc tính hơn là cần phải được truy cập theo cùng một cách (tôi thích tính nhất quán).

Cách thứ nhất có thể, tôi có thể làm điều này là sử dụng chỉ getter và setter:

class Eggs(object): 

    def __init__(self): 
     self._spam = '' 
     self.set_spam('Monster') 
     print self.get_spam() 
     spam_button.bind('<Enter>', lambda: self.set_spam('Ouch')) 

    def set_spam(self, spam): 
     if len(spam) <= 32: 
      self._spam = spam 

    def get_spam(self): 
     return self._spam 

Nhưng nếu tôi sử dụng phương pháp này và có rất nhiều thuộc tính, các mã có thể có được nhiều thời gian để quản lý.

Cách thứ hai tôi có thể làm điều này là sử dụng tài sản, và sử dụng các setter trong callback:

class Eggs(object): 

    def __init__(self): 
     self._spam = '' 
     self.spam = 'Monster' 
     print self.spam 
     spam_button.bind('<Enter>', lambda: self.set_spam('Ouch')) 

    def set_spam(self, spam): 
     if len(spam) <= 32: 
      self._spam = spam 

    def get_spam(self): 
     return self._spam 

    spam = property(get_spam, set_spam) 

Bằng cách này là một chút đơn giản hơn là người đầu tiên khi truy cập vào thuộc tính trực tiếp, nhưng không phù hợp khi sử dụng lambda.

Cách thứ ba để làm điều này sẽ làm cho một lớp học bổ sung với get và thiết lập các phương pháp:

class Spam(object): 

    def __init__(self): 
     self._value = '' 

    def set(self, value): 
     if len(spam) <= 32: 
      self._value = value 

    def get(self): 
     return self._value 


class Eggs(object): 

    def __init__(self): 
     self._spam = '' 
     self.spam = Spam() 
     self.spam.set('Monster') 
     print self.spam.get() 
     spam_button.bind('<Enter>', lambda: self.spam.set('Ouch')) 

Phương pháp này là tổ chức hơn là người đầu tiên, nhưng tôi sẽ cần phải thực hiện một lớp đối với từng loại xác thực.

Cách cuối cùng tôi có thể làm điều đó là phương pháp sử dụng thay vì tài sản (bất động sản là những ví dụ thứ hai):

class Eggs(object): 

    def __init__(self): 
     self._spam = '' 
     self.spam('Monster') 
     print self.spam() 
     spam_button.bind('<Enter>', lambda: self.spam('Ouch')) 

    def spam(self, spam=None): 
     if spam != None: 
      if len(spam) <= 32: 
       self._spam = spam 
     else: 
      return self._spam 

Phương pháp này có lẽ sẽ là ngắn nhất, nhưng nó là khó khăn hơn trong nháy mắt để trả lời liệu Tôi đang nhận hoặc thiết lập.

Phương pháp nào (nếu có) của những phương pháp này được ưu tiên?

Trả lời

2

Sử dụng một đóng cửa để trả lại một chức năng được sử dụng như một callback, và xác định mỗi trạng như một chức năng (sử dụng lambda hoặc def, sở thích của bạn) thay vì mỗi setter như một hàm.

class Eggs(object): 

    def __init__(self): 
     self.spam = 'Monster' 
     def spam_condition(string = None): 
      return (string is not None) and (len(string) <= 32) 
     self.spam_setter = self.set('spam', 'Ouch', spam_condition) 
     spam_button.bind('<Enter>', self.spam_setter) 
     self.spam_setter(val='Horse') 


    def set(self, name, value, condition): 
     def setter(val): 
      if type(val) is not .... : # fill this in with the TYPE of the event 
       value = val 
      if condition(value): 
       setattr(self, name, value) 
     return setter 

Chỉnh sửa: Bây giờ bạn có thể gọi setter để kiểm tra điều kiện, từ mọi nơi.

Chỉnh sửa 2: Bằng cách này, trình cài đặt sẽ kiểm tra xem nó có sự kiện không và nếu không, hãy sử dụng giá trị được chuyển. Bạn cũng có thể sử dụng:

if not isinstance(val, ....) : # fill this in with the CLASS of the event 
+0

Cảm ơn ý tưởng, nhưng tôi không thực sự muốn sử dụng chuỗi để truy cập thuộc tính (tôi cũng có thể sử dụng từ điển) và điều này không cung cấp cách để đặt và xác thực giá trị bên ngoài một ràng buộc (trừ khi tôi thực hiện 'self.set ('spam', 'Ouch', spam_condition)()' mỗi khi tôi muốn đặt giá trị, điều này thực sự xấu). Ngoài ra, bạn sẽ muốn thêm tham số 'event' vào hàm' setter', nếu không Tkinter không thể chuyển sự kiện (và nhận 'ValueError'). –

+0

Bây giờ thiết lập có thể được sử dụng từ bất cứ nơi nào. – agf

+0

Nếu tôi sử dụng 'lambda e: self.spam_setter()' làm gọi lại và loại bỏ tham số 'event' khỏi' setter', tôi không cần phải xác định 'val =' mỗi lần tôi đặt giá trị. Có thể loại bỏ 'e' khỏi' lambda e: 'trong hàm gọi lại không? –

1

Tôi vẫn đề nghị sử dụng dict và gọi nó là một ngày.Phân lớp nó và ghi đè __setitem__ để làm xác nhận dữ liệu của bạn, sau đó đừng lo lắng về thu khí:

#!/usr/bin/env python 

class Egg(dict): 
    def __init__(self, *args, **kwargs): 
     super(Egg, self).__init__(*args, **kwargs) 
     self['spam'] = 'foo' 
     spam_button.bind('<Enter>', lambda: self.__setitem__('spam', 'Ouch')) 

    def __setitem__(self, key, value): 
     if key == 'spam': 
      if len(value) > 32: 
       raise ValueError('"%s" is longer than 32 characters') 
      return super(Egg, self).__setitem__(key, value) 
     raise KeyError(key) 
0

Vấn đề xác nhận có thể bị xử lý bằng một property:

class Egg(object): 
    @property 
    def spam(self): 
     return self._spam 

    @spam.setter 
    def spam(self, value):  
     if len(value) <= 32: 
      self._spam = value 

Rõ ràng, bạn vẫn có thể sử dụng self._spam = 'spam '*10+'baked beans and spam' với sự bất công.

Sử dụng được xây dựng trong setattr:

lambda: setattr(self, 'spam', 'Ouch') 

Nếu bạn phản đối ..."spam"... và thích chỉ ...spam..., bạn có thể sử dụng phương pháp property:

lambda: self.__class__.spam.fset(self, 'Ouch') 

hay, kể từ khi property là một mô tả:

lambda: type(self).spam.__set__(self, 'Ouch') 

Nhưng phiên bản đầu tiên được ưa thích, tôi hy vọng vì những lý do rõ ràng.

+0

Anh ấy muốn tự động kiểm tra điều kiện bất cứ khi nào anh ta đặt spam, vì vậy chỉ cần sử dụng 'setattr' không giúp được gì. – agf

0

Tôi thích phương pháp dựa trên lớp học. Có thể bạn sẽ có một bộ xác nhận hợp lệ mà bạn muốn thực hiện (ví dụ chiều dài tối đa của một chuỗi, phạm vi hợp lệ cho một số, v.v.), do đó bạn sẽ có một số lượng giới hạn các lớp.

Ví dụ:

class LimitedLengthString(object): 
    def __init__(self, max_length): 
     self.max_length = max_length 
    def set(self, value): 
     if len(value) <= self.max_length: 
      self.value = value 
    def get(self): 
     return value 

class Eggs(object): 
    def __init__(self): 
     self.spam = LimitedLengthString(32) 
     self.spam.set('Monster') 
     print self.spam.get() 
     spam_button.bind('<Enter>', lambda: self.spam.set('Ouch')) 

Nếu bạn thực sự có kiểm chứng thực duy nhất mà cần phải được thực hiện để các thuộc tính khác nhau, bạn không thể thoát khỏi viết rất nhiều mã. Nhưng như mọi khi trong lập trình, tổng quát khi bạn bắt đầu lặp lại chính mình.


Cập nhật sau khi gợi ý bởi TokenMacGuy: Alternative sử dụng tính năng Python khá rõ "mô tả":

class LimitedLengthString(object): 
    def __init__(self, name, max_length): 
     self.name = name 
     self.max_length = max_length 

    def __set__(self, instance, value): 
     if len(value) <= self.max_length: 
      instance.__dict__[self.name] = value 

    def __get__(self, instance, owner): 
     return instance.__dict__[self.name] 

class Eggs(object): 
    spam = LimitedLengthString('spam', 32) 
    def __init__(self): 
     self.spam = 'Monster' 
     print self.spam # prints 'Monster' 
     self.spam = 'x' * 40 
     print self.spam # still 'Monster' 
     spam_button.bind('<Enter>', lambda: self.spam = 'Ouch') 

Tôi tìm thấy một pretty good introduction to descriptors.

+0

tại sao không triển khai giao thức mô tả? (http://docs.python.org/reference/datamodel.html#descriptors) – SingleNegationElimination

+0

@TokenMacGuy: Điểm tốt. Đã cập nhật câu trả lời của tôi. –