2009-11-19 50 views
97

Tôi đang cố gắng hiểu phạm vi trong các lớp lồng nhau trong Python. Đây là mã ví dụ của tôi:Phạm vi của các lớp lồng nhau?

class OuterClass: 
    outer_var = 1 
    class InnerClass: 
     inner_var = outer_var 

Việc tạo ra lớp không đầy đủ và tôi nhận được lỗi:

<type 'exceptions.NameError'>: name 'outer_var' is not defined 

Cố inner_var = Outerclass.outer_var không hoạt động. tôi nhận được:

<type 'exceptions.NameError'>: name 'OuterClass' is not defined 

Tôi cố gắng để truy cập vào tĩnh outer_var từ InnerClass.

Có cách nào để thực hiện việc này không?

Trả lời

98
class Outer(object): 
    outer_var = 1 

    class Inner(object): 
     @property 
     def inner_var(self): 
      return Outer.outer_var 

Điều này không hoàn toàn giống với những thứ tương tự làm việc trong các ngôn ngữ khác và sử dụng tra cứu toàn cầu thay vì truy cập vào outer_var. (Nếu bạn thay đổi những gì đối tượng tên Outer là ràng buộc để, sau đó mã này sẽ sử dụng đối tượng lần sau khi nó được thực thi.)

Nếu bạn thay vì muốn tất cả Inner đối tượng để có một tham chiếu đến một Outerouter_var thực sự là một thuộc tính dụ:

class Outer(object): 
    def __init__(self): 
     self.outer_var = 1 

    def get_inner(self): 
     return self.Inner(self) 
     # "self.Inner" is because Inner is a class attribute of this class 
     # "Outer.Inner" would also work, or move Inner to global scope 
     # and then just use "Inner" 

    class Inner(object): 
     def __init__(self, outer): 
      self.outer = outer 

     @property 
     def inner_var(self): 
      return self.outer.outer_var 

Lưu ý rằng các lớp lồng có phần không phổ biến trong Python và không tự động ám chỉ bất kỳ mối quan hệ đặc biệt nào giữa các lớp. Bạn tốt hơn không làm tổ. (Bạn vẫn có thể đặt thuộc tính lớp trên Outer thành Inner, nếu bạn muốn.)

+1

Nó có thể là hữu ích để thêm với phiên bản (s) của python câu trả lời của bạn sẽ làm việc. –

+1

Tôi đã viết điều này với 2,6/2.x trong tâm trí, nhưng, nhìn vào nó, tôi thấy không có gì mà sẽ không làm việc giống nhau trong 3.x. –

+0

Tôi không hoàn toàn hiểu ý của bạn trong phần này, "(Nếu bạn thay đổi đối tượng tên Outer bị ràng buộc, thì mã này sẽ sử dụng đối tượng đó vào lần sau nó được thực hiện.)" Bạn có thể vui lòng giúp tôi hiểu không ? – batbrat

20

Bạn có thể nên tắt nếu bạn không sử dụng các lớp lồng nhau. Nếu bạn phải làm tổ, hãy thử này:

x = 1 
class OuterClass: 
    outer_var = x 
    class InnerClass: 
     inner_var = x 

Hoặc tuyên bố cả hai lớp trước khi làm tổ họ: (. Sau này bạn có thể del InnerClass nếu bạn cần)

class OuterClass: 
    outer_var = 1 

class InnerClass: 
    inner_var = OuterClass.outer_var 

OuterClass.InnerClass = InnerClass 

40

Tôi nghĩ rằng bạn có thể chỉ cần làm:

class OuterClass: 
    outer_var = 1 

    class InnerClass: 
     pass 
    InnerClass.inner_var = outer_var 

Sự cố bạn gặp phải là do:

A block is a piece of Python program text that is executed as a unit. The following are blocks: a module, a function body, and a class definition.
(...)
A scope defines the visibility of a name within a block.
(...)
The scope of names defined in a class block is limited to the class block; it does not extend to the code blocks of methods – this includes generator expressions since they are implemented using a function scope. This means that the following will fail:

class A: 

     a = 42 

     b = list(a + i for i in range(10)) 

http://docs.python.org/reference/executionmodel.html#naming-and-binding

Các phương tiện trên:
một cơ quan chức năng là một khối mã và một phương pháp là một chức năng, sau đó được xác định tên ra khỏi cơ thể chức năng hiện diện trong một định nghĩa lớp không mở rộng đến các cơ quan chức năng.

Diễn giải trường hợp này:
định nghĩa lớp là khối mã, sau đó tên được xác định trong định nghĩa lớp bên trong có định nghĩa lớp ngoài không mở rộng định nghĩa lớp bên trong.

+2

Tuyệt vời. Ví dụ của bạn không thành công, tuyên bố "tên chung" a "không được xác định". Tuy nhiên, thay thế một danh sách hiểu '[a + i cho i trong phạm vi (10)]' thành công liên kết A.b với danh sách dự kiến ​​[42..51]. – George

+5

@George Lưu ý rằng ví dụ với ** lớp A ** không phải của tôi, nó là từ tài liệu chính thức của Python mà tôi đã cung cấp liên kết. Ví dụ này thất bại và thất bại đó là những gì muốn được hiển thị trong ví dụ này. Trong thực tế 'danh sách (a + i cho i trong phạm vi (10))' 'là' 'danh sách ((a + i cho i trong phạm vi (10)))' 'đó là để nói' 'danh sách (a_generator)' '. Họ nói ** một máy phát ** được thực hiện với phạm vi tương tự hơn phạm vi chức năng. – eyquem

+0

@George Đối với tôi, điều đó có nghĩa là các chức năng hoạt động khác nhau tùy theo chúng ở trong một mô-đun hoặc trong một lớp học. Trong trường hợp đầu tiên, một hàm đi ra ngoài để tìm đối tượng được liên kết với một số nhận dạng miễn phí. Trong trường hợp thứ hai, một hàm, đó là nói một phương thức, không đi ra ngoài cơ thể của nó. Các hàm trong một mô đun và các phương thức trong một lớp là trong thực tế hai loại đối tượng. Các phương thức không chỉ là các hàm trong lớp. Đó là ý tưởng của tôi. – eyquem

4

giải pháp dễ nhất:

class OuterClass: 
    outer_var = 1 
    class InnerClass: 
     def __init__(self): 
      self.inner_var = OuterClass.outer_var 

Nó đòi hỏi bạn phải rõ ràng, nhưng không mất nhiều công sức.

+0

TênError: tên 'OuterClass' không được xác định - -1 –

+0

@Mr_and_Mrs_D Vâng, hãy xem chỉnh sửa. –

+1

Vấn đề là truy cập nó từ phạm vi lớp bên ngoài - cộng với lỗi tương tự sẽ xảy ra nếu bạn gọi nó từ một phạm vi tĩnh bên trong bên ngoài. Tôi khuyên bạn nên xóa bài đăng này –

1

Trong các đối tượng có thể thay đổi Python được chuyển làm tham chiếu, vì vậy bạn có thể chuyển tham chiếu của lớp ngoài tới lớp bên trong.

class OuterClass: 
    def __init__(self): 
     self.outer_var = 1 
     self.inner_class = OuterClass.InnerClass(self) 
     print('Inner variable in OuterClass = %d' % self.inner_class.inner_var) 

    class InnerClass: 
     def __init__(self, outer_class): 
      self.outer_class = outer_class 
      self.inner_var = 2 
      print('Outer variable in InnerClass = %d' % self.outer_class.outer_var) 
+1

Xin lưu ý rằng bạn có chu kỳ tham chiếu tại đây và trong một số trường hợp kịch bản của lớp học này sẽ không được giải phóng. Một ví dụ, với cPython, nếu bạn đã định nghĩa phương thức '__del__', bộ gom rác sẽ không thể xử lý chu trình tham chiếu, và các đối tượng sẽ đi vào' gc.garbage'. Mã trên, như là, là không có vấn đề mặc dù. Cách xử lý bằng cách sử dụng ** tham chiếu yếu **. Bạn có thể đọc tài liệu về [weakref (2.7)] (https://docs.python.org/2/library/weakref.html) hoặc [weakref (3.5)] (https://docs.python.org/3.5/ library/weakref.html) – guyarad

0

Tất cả các giải thích có thể được tìm thấy trong Python Documentation Các Bài chỉ dẫn Python

Đối với lỗi đầu tiên bạn <type 'exceptions.NameError'>: name 'outer_var' is not defined. Lời giải thích là:

There is no shorthand for referencing data attributes (or other methods!) from within methods. I find that this actually increases the readability of methods: there is no chance of confusing local variables and instance variables when glancing through a method.

trích dẫn từ Các Bài chỉ dẫn Python 9,4

Đối với lỗi thứ hai của bạn <type 'exceptions.NameError'>: name 'OuterClass' is not defined

When a class definition is left normally (via the end), a class object is created.

trích dẫn từ Các Bài chỉ dẫn Python 9.3.1

Vì vậy, khi bạn thử inner_var = Outerclass.outer_var, Quterclass đã chưa được khởi tạo, đó là lý do tại sao name 'OuterClass' is not defined

Một chi tiết hơn nhưng tẻ nhạt giải thích cho lỗi đầu tiên của bạn:

Although classes have access to enclosing functions’ scopes, though, they do not act as enclosing scopes to code nested within the class: Python searches enclosing functions for referenced names, but never any enclosing classes. That is, a class is a local scope and has access to enclosing local scopes, but it does not serve as an enclosing local scope to further nested code.

trích dẫn từ Learning.Python (5) .Mark.Lutz

Các vấn đề liên quan