Cách Pythonic làm bí danh thành phần


8

Cách pythonic và chính xác nhất để làm bí danh thành phần là gì?

Đây là một tình huống giả định:

class House:
    def cleanup(self, arg1, arg2, kwarg1=False):
        # do something

class Person:
    def __init__(self, house): 
        self.house = house
        # aliases house.cleanup
        # 1.
        self.cleanup_house = self.house.cleanup

    # 2.
    def cleanup_house(self, arg1, arg2, kwarg1=False):
        return self.house.cleanup(arg1=arg1, arg2=arg2, kwarg1=kwarg1)

AFAIK với # 1 các biên tập viên được thử nghiệm của tôi hiểu những điều này cũng tốt như # 2 - hoàn thành tự động, chuỗi doc, v.v.

Có bất kỳ nhược điểm nào đối với cách tiếp cận số 1 không? Cách nào đúng hơn theo quan điểm của python?

Để mở rộng phương pháp số 1 không mong muốn và loại biến thể được gợi ý sẽ miễn nhiễm với tất cả các vấn đề được nêu trong các nhận xét:

class House:

    def cleanup(self, arg1, arg2, kwarg1=False):
        """clean house is nice to live in!"""
        pass


class Person:
    def __init__(self, house: House):
        self._house = house
        # aliases
        self.cleanup_house = self.house.cleanup

    @property
    def house(self):
        return self._house

5
Nếu một người có một ngôi nhà mới thì sao?
user2357112 hỗ trợ Monica

Không phải trong nền kinh tế này! Đùa qua một bên tôi đoán làm cho thuộc tính house không thể và thêm một setter sẽ làm điều đó. Câu hỏi bây giờ là cái nào dễ bảo trì hơn - cái này hay viết lại chữ ký của nhiều phương pháp?
Granitosaurus

2
# 2 phù hợp với những gì người ta mong muốn bằng trực giác Person.cleanup_house. Nó có được ngôi nhà liên kết với người đó, và dọn dẹp nó. # 1 sẽ dọn dẹp một số ngôi nhà, và tại một số thời điểm (khởi tạo), ngôi nhà đó là chính xác. Nó không đối phó tốt với Người thay đổi nhà cửa, hoặc nếu một Người không có nhà
Bài thánh ca cho vũ trường

Câu trả lời:


8

Có một số vấn đề với phương pháp đầu tiên:

  1. Bí danh sẽ không cập nhật khi thuộc tính mà nó đề cập đến thay đổi trừ khi bạn nhảy qua các vòng bổ sung. Ví dụ, bạn có thể tạo housemột propertybộ cài đặt, nhưng đó là công việc không tầm thường đối với những thứ không nên yêu cầu. Xem phần cuối của câu trả lời này để thực hiện mẫu.
  2. cleanup_housesẽ không được thừa kế. Một đối tượng hàm được định nghĩa trong một lớp là một mô tả không dữ liệu có thể được kế thừa và ghi đè, cũng như bị ràng buộc với một thể hiện. Một thuộc tính thể hiện như trong cách tiếp cận đầu tiên hoàn toàn không có trong lớp. Thực tế rằng nó là một phương pháp ràng buộc là ngẫu nhiên. Một lớp con sẽ không thể truy cập super().cleanup_house, ví dụ cụ thể.
  3. person.cleanup_house.__name__ != 'cleanup_house'. Đây không phải là thứ bạn kiểm tra thường xuyên, nhưng khi bạn làm, bạn sẽ mong đợi tên hàm là cleanup.

Tin vui là bạn không phải lặp lại chữ ký nhiều lần để sử dụng phương pháp số 2. Python cung cấp ký hiệu splat ( *) / splatty-splat ( **) rất thuận tiện để ủy quyền tất cả kiểm tra đối số cho phương thức được gói:

def cleanup_house(self, *args, **kwargs):
    return self.house.cleanup(*args, **kwargs)

Và đó là nó. Tất cả các đối số thông thường và mặc định được truyền qua nguyên trạng.

Đây là lý do mà # 2 là cách tiếp cận pythonic nhiều hơn. Tôi không biết làm thế nào nó sẽ tương tác với các biên tập viên hỗ trợ gợi ý loại trừ khi bạn sao chép chữ ký phương thức.

Một điều có thể là một vấn đề cleanup_house.__doc__là không giống như house.cleanup.__doc__. Điều này có khả năng có thể chuyển đổi housethành một property, mà setter gán cleanup_house.__doc__.


Để giải quyết vấn đề 1. (chứ không phải 2. hoặc 3.), bạn có thể triển khai housenhư một thuộc tính với setter. Ý tưởng là cập nhật các bí danh bất cứ khi nào housethuộc tính thay đổi. Đây không phải là một ý tưởng tốt nói chung, nhưng đây là một triển khai thay thế cho những gì bạn có trong câu hỏi có khả năng sẽ hoạt động tốt hơn một chút:

class House:
    def cleanup(self, arg1, arg2, kwarg1=False):
        """clean house is nice to live in!"""
        pass


class Person:
    def __init__(self, house: House):
        self.house = house  # use the property here

    @property
    def house(self):
        return self._house

    @house.setter
    def house(self, value):
        self._house = house
        self.cleanup_house = self._house.cleanup

Tuy nhiên, *args, **kwargscác tính năng hoàn thành tự động phá vỡ và nói chung là rất khó để làm việc với. Bạn cũng cần xác định tài liệu hai lần cho house.cleanupperson.cleanup_househoặc hack chúng bằng cách nào đó. Vì vậy, với phương pháp này, bạn cần liên tục duy trì chữ ký và tài liệu bằng tay có thể là rất nhiều công việc trong các chương trình lớn.
Granitosaurus

2
@Granitosaurus. Tôi không chắc làm thế nào *args**kwargslúng túng. Họ thực sự ở đó vì vậy bạn không cần phải duy trì chữ ký. __doc__có thể được duy trì bằng cách chuyển nhượng trực tiếp.
Nhà vật lý điên

cũng tranh luận và kwargs làm xáo trộn chữ ký và các biên tập viên không biết các giá trị mong đợi là gì - hơi khó xử lý. Mặc dù có vẻ như vấn đề này có thể được kinda giải quyết bằng cách thêm functool.wraps(<parent_func>)trang trí, mặc dù giới thiệu rằng rất nhiều mâu thuẫn là tốt.
Granitosaurus

Tôi thực sự thích cách tiếp cận được đề xuất của bạn với setters! Tôi vừa thử nghiệm nó và nó có đầy đủ chức năng với các chức năng soạn thảo dự kiến ​​(tự động hoàn thành, chữ ký và tài liệu, ít nhất là trên PyCharm)!
Granitosaurus

@Granitosaurus. Miễn là bạn nhận thức được các vấn đề chức năng, cách tiếp cận bạn chọn hoàn toàn là do bạn quyết định.
Nhà vật lý điên

4

Tôi chỉ muốn thêm một cách tiếp cận ở đây, nếu bạn muốn housecông khai và ổn định (tôi thường coi điều đó là bất biến), bạn có thể tạo cleanup_housetài sản, như vậy:

class House:
    def cleanup(self, arg1, arg2, kwarg1=False):
        """clean house is nice to live in!"""
        print('cleaning house')


class Person:
    def __init__(self, house: House):
        self.house = house

    @property
    def cleanup_house(self):
        return self.house.cleanup

Ít nhất là trong REPL của Ipython, chuỗi hoàn thành mã và chuỗi có vẻ hoạt động như bạn mong muốn. Lưu ý chắc chắn cách nó sẽ tương tác với các chú thích loại ...

EDIT: vì vậy, mypy 0.740 ít nhất không thể suy ra chữ ký loại person.cleanup_house, vì vậy điều đó không tuyệt vời, mặc dù, nó không đáng ngạc nhiên:

(py38) Juans-MBP:workspace juan$ cat test_mypy.py
class House:
    def cleanup(self, arg1:int, arg2:bool):
        """clean house is nice to live in!"""
        print('cleaning house')


class Person:
    house: House
    def __init__(self, house: House):
        self.house = house  # use the property here

    @property
    def cleanup_house(self):
        return self.house.cleanup

person = Person(House())
person.cleanup_house(1, True)
person.cleanup_house('Foo', 'Bar')
reveal_type(person.cleanup_house)
reveal_type(person.house.cleanup)
(py38) Juans-MBP:workspace juan$ mypy test_mypy.py
test_mypy.py:19: note: Revealed type is 'Any'
test_mypy.py:20: note: Revealed type is 'def (arg1: builtins.int, arg2: builtins.bool) -> Any'

Tôi vẫn sẽ chỉ đi với # 2.


1
Điều này đánh bại ý tưởng của tôi về việc viết một bộ mô tả không dữ liệu để hoạt động như một hàm bao bọc nhưng cũng chuyển tiếp các chú thích và chữ ký của mục tiêu.
Nhà vật lý điên
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.