Biến phiên bản so với biến lớp trong Python


121

Tôi có các lớp Python, trong đó tôi chỉ cần một cá thể trong thời gian chạy, vì vậy sẽ là đủ nếu chỉ có các thuộc tính một lần cho mỗi lớp chứ không phải cho mỗi trường hợp. Nếu có nhiều hơn một phiên bản (điều này sẽ không xảy ra), tất cả phiên bản phải có cùng một cấu hình. Tôi tự hỏi tùy chọn nào sau đây sẽ tốt hơn hoặc Python "thành ngữ" hơn.

Biến lớp:

class MyController(Controller):

  path = "something/"
  children = [AController, BController]

  def action(self, request):
    pass

Biến thể hiện:

class MyController(Controller):

  def __init__(self):
    self.path = "something/"
    self.children = [AController, BController]

  def action(self, request):
    pass

4
Sau khi đọc câu hỏi này và xem câu trả lời, một trong những câu hỏi đầu tiên của tôi là, "Vậy làm cách nào để truy cập các biến lớp?" - đó là bởi vì cho đến thời điểm này tôi chỉ sử dụng các biến cá thể. Để trả lời cho câu hỏi của riêng tôi, bạn làm điều đó thông qua chính tên lớp, mặc dù về mặt kỹ thuật, bạn cũng có thể làm điều đó thông qua một phiên bản. Dưới đây là một liên kết để đọc cho bất cứ ai khác với cùng một câu hỏi: stackoverflow.com/a/3434596/4561887
Gabriel Staples

Câu trả lời:


159

Nếu bạn chỉ có một trường hợp, tốt nhất là tạo tất cả các biến cho mỗi trường hợp, đơn giản vì chúng sẽ được truy cập (một chút) nhanh hơn (một mức độ "tra cứu" ít hơn do "kế thừa" từ lớp này sang trường hợp khác), và không có nhược điểm nào cân bằng với lợi thế nhỏ này.


7
Bạn chưa bao giờ nghe nói về mô hình Borg? Chỉ có một trường hợp là sai cách để có nó ngay từ đầu.
Devin Jeanpierre

434
@Devin, vâng, tôi đã nghe nói về mô hình Borg, vì tôi là người đã giới thiệu nó (vào năm 2001, cfr code.activestate.com/recipes/… ;-). Nhưng không có gì sai, trong những trường hợp đơn giản, chỉ cần có một trường hợp duy nhất mà không cần thực thi.
Alex Martelli

2
@ user1767754, dễ dàng tự tạo chúng python -mtimeit- nhưng vừa mới làm như vậy trong python3.4 Tôi lưu ý rằng việc truy cập vào một intbiến lớp thực sự nhanh hơn khoảng 5 đến 11 nano giây so với biến cá thể trên máy trạm cũ của tôi - không chắc chắn điều gì codepath làm cho nó như vậy.
Alex Martelli

45

Tiếp tục lặp lại lời khuyên của MikeAlex và thêm màu sắc của riêng tôi ...

Sử dụng các thuộc tính instance là một điển hình ... thì Python càng dễ hiểu. Thuộc tính lớp không được sử dụng nhiều, vì các trường hợp sử dụng của chúng là cụ thể. Điều này cũng đúng đối với phương thức tĩnh và phương thức lớp so với phương thức "bình thường". Chúng là các cấu trúc đặc biệt giải quyết các trường hợp sử dụng cụ thể, nếu không, đó là mã được tạo bởi một lập trình viên không ngoan muốn chứng tỏ rằng họ biết một số góc khuất của lập trình Python.

Alex đề cập trong câu trả lời của mình rằng truy cập sẽ nhanh hơn (một chút) do ít cấp độ tra cứu hơn ... hãy để tôi làm rõ thêm cho những người chưa biết về cách hoạt động của điều này. Nó rất giống với truy cập biến - thứ tự tìm kiếm là:

  1. dân địa phương
  2. người không định cư
  3. quả cầu
  4. tích hợp sẵn

Đối với quyền truy cập thuộc tính, thứ tự là:

  1. ví dụ
  2. lớp học
  3. các lớp cơ sở được xác định bởi MRO (thứ tự phân giải phương thức)

Cả hai kỹ thuật đều hoạt động theo cách "từ trong ra ngoài", có nghĩa là hầu hết các đối tượng cục bộ được kiểm tra đầu tiên, sau đó các lớp bên ngoài được kiểm tra liên tiếp.

Trong ví dụ của bạn ở trên, giả sử bạn đang tìm kiếm paththuộc tính. Khi nó gặp một tham chiếu như " self.path", Python sẽ xem xét các thuộc tính cá thể đầu tiên để tìm một kết quả phù hợp. Khi không thành công, nó sẽ kiểm tra lớp mà đối tượng được khởi tạo từ đó. Cuối cùng, nó sẽ tìm kiếm các lớp cơ sở. Như Alex đã nêu, nếu thuộc tính của bạn được tìm thấy trong trường hợp này, nó không cần phải tìm ở nơi khác, do đó bạn sẽ tiết kiệm được một chút thời gian.

Tuy nhiên, nếu bạn nhấn mạnh vào các thuộc tính của lớp, bạn cần phải tra cứu thêm. Hoặc , cách thay thế khác của bạn là tham chiếu đến đối tượng thông qua lớp thay vì đối tượng, ví dụ: MyController.paththay vì self.path. Đó là cách tra cứu trực tiếp sẽ xoay quanh quá trình tra cứu trì hoãn, nhưng như alex đề cập bên dưới, đó là một biến toàn cục, vì vậy bạn sẽ mất bit mà bạn nghĩ rằng bạn sẽ lưu (trừ khi bạn tạo một tham chiếu cục bộ cho tên lớp [global] ).

Điểm mấu chốt là bạn nên sử dụng các thuộc tính cá thể hầu hết thời gian. Tuy nhiên, sẽ có những trường hợp thuộc tính lớp là công cụ phù hợp cho công việc. Mã sử ​​dụng cả hai cùng một lúc sẽ đòi hỏi sự siêng năng nhất, bởi vì việc sử dụng selfsẽ chỉ giúp bạn có được đối tượng thuộc tính cá thể và bóng đổ truy cập vào thuộc tính lớp có cùng tên. Trong trường hợp này, bạn phải sử dụng quyền truy cập thuộc tính theo tên lớp để tham chiếu đến nó.


@wescpy, nhưng MyControllerlà nhìn lên trong globals, vì vậy tổng chi phí cao hơn self.pathnơi pathlà một biến Ví dụ (vì selfđịa phương với phương pháp == tra cứu siêu nhanh).
Alex Martelli,

à, đúng. nắm bắt tốt. Tôi đoán cách giải quyết duy nhất là tạo một tham chiếu cục bộ ... tại thời điểm này, nó không thực sự đáng giá.
wescpy

24

Khi nghi ngờ, bạn có thể muốn một thuộc tính thể hiện.

Các thuộc tính lớp tốt nhất được dành riêng cho các trường hợp đặc biệt mà chúng có ý nghĩa. Trường hợp sử dụng rất phổ biến duy nhất là các phương thức. Không có gì lạ khi sử dụng thuộc tính lớp cho các hằng số chỉ đọc mà các cá thể cần biết (mặc dù lợi ích duy nhất của điều này là nếu bạn cũng muốn truy cập từ bên ngoài lớp), nhưng bạn chắc chắn nên thận trọng khi lưu trữ bất kỳ trạng thái nào trong chúng , hiếm khi là thứ bạn muốn. Ngay cả khi bạn chỉ có một cá thể, bạn nên viết lớp giống như bạn làm với bất kỳ trường hợp nào khác, điều này thường có nghĩa là sử dụng các thuộc tính cá thể.


1
Các biến lớp là một loại hằng số chỉ đọc. Nếu Python cho phép tôi định nghĩa hằng số, tôi sẽ viết nó dưới dạng hằng số.
ngừng giảng

1
@deamon, tôi có nhiều khả năng đặt các hằng số của mình hoàn toàn bên ngoài định nghĩa lớp và đặt tên chúng bằng tất cả các chữ hoa. Đưa chúng vào trong lớp cũng tốt. Làm cho chúng thuộc tính cá thể sẽ không ảnh hưởng gì, nhưng có thể hơi kỳ lạ. Tôi không nghĩ đây là vấn đề mà cộng đồng đứng sau một trong những lựa chọn quá nhiều.
Mike Graham

@MikeGraham FWIW, Hướng dẫn kiểu Python của Google đề xuất tránh các biến toàn cục thay cho các biến lớp. Tuy nhiên, vẫn có những ngoại lệ.
Dennis

Đây là một liên kết mới đến Hướng dẫn kiểu Python của Google . Bây giờ có một cách viết đơn giản: avoid global variablesvà định nghĩa của chúng là, các biến toàn cục cũng là các biến được khai báo là thuộc tính lớp. Tuy nhiên, hướng dẫn kiểu riêng của Python ( PEP-8 ) nên là nơi đầu tiên dành cho các câu hỏi dạng này. Sau đó, tâm trí của chính bạn sẽ là công cụ lựa chọn (tất nhiên bạn cũng có thể lấy ý tưởng từ Google chẳng hạn).
colidyre vào

4

Câu hỏi tương tự tại Hiệu suất truy cập các biến lớp trong Python - mã ở đây được điều chỉnh từ @Edward Loper

Biến cục bộ là biến nhanh nhất để truy cập, được gắn khá nhiều với Biến mô-đun, tiếp theo là Biến lớp, tiếp theo là Biến phiên bản.

Có 4 phạm vi bạn có thể truy cập các biến từ:

  1. Biến phiên bản (self.varname)
  2. Biến lớp (Classname.varname)
  3. Biến mô-đun (VARNAME)
  4. Biến cục bộ (varname)

Các bài kiểm tra:

import timeit

setup='''
XGLOBAL= 5
class A:
    xclass = 5
    def __init__(self):
        self.xinstance = 5
    def f1(self):
        xlocal = 5
        x = self.xinstance
    def f2(self):
        xlocal = 5
        x = A.xclass
    def f3(self):
        xlocal = 5
        x = XGLOBAL
    def f4(self):
        xlocal = 5
        x = xlocal
a = A()
'''
print('access via instance variable: %.3f' % timeit.timeit('a.f1()', setup=setup, number=300000000) )
print('access via class variable: %.3f' % timeit.timeit('a.f2()', setup=setup, number=300000000) )
print('access via module variable: %.3f' % timeit.timeit('a.f3()', setup=setup, number=300000000) )
print('access via local variable: %.3f' % timeit.timeit('a.f4()', setup=setup, number=300000000) )

Kết quả:

access via instance variable: 93.456
access via class variable: 82.169
access via module variable: 72.634
access via local variable: 72.199
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.