Repository: docarray/docarray

Issue:
docs: add docs for the dict() method
# Context

we need to document the behavior of calling `dict()` on a BaseDoc. Especially with nested DocList.

```python
class InnerDoc(BaseDoc):
    x: int

class MyDoc(BaseDoc):
    l: DocList[InnerDoc]

doc = MyDoc(l=[InnerDoc(x=1), InnerDoc(x=2)])
print(f'{doc.dict()=}')
#> doc_view.dict()={'id': 'ab65fc52a60d1da1ce6196c100943e1f', 'l': [{'id': 'e218c3a6904cbbd0f48ccd10130c9f78', 'x': 1}, {'id': 'e30bf3d613bf5c3e805faf0c0dc0f704', 'x': 2}]}
```


and we need to explain how using `dict(doc)` is different from using `doc.dict()`


Discussion:
Hi, can I take this issue up? I would like to contribute 
Hey @shivance ,

For sure, I will assign it to you.
Hi. I can take it up if a fix for this has not already been merged. I went through the library and looked at the `BaseDoc` class and had a couple of questions
1. Would I be documenting the behaviour of [this](https://github.com/docarray/docarray/blob/29c2d23a618704e9ed13108c754e09c6ef053a93/docarray/base_doc/doc.py#L301) class method inside `BaseDoc`
2. From what I can understand, if `dict(doc)` is called on a `BaseDoc` object it should return the attributes set and calling `doc.dict()` would return model created as shown in the issue description.
3. Can you guide me on what the appropriate documentation would be?
@punndcoder28 You could take this up ! Sorry, I've got a bit busy lately, it would be delayed at my end.
@punndcoder28 I suggest you start by writing the a small documentation example in this issue. Something with code snippets to explain the behavior. Then I will point you where you should add it in the documentation
Sure @samsja will do that. I want to know if I have understood the working of both the methods as mentioned earlier in [this](https://github.com/docarray/docarray/issues/1560#issuecomment-1574760721) comment. If that is confirmed will work on writing a draft changes here along with the code changes for your reviews.
> if dict(doc) is called on a BaseDoc object it should return the attributes set

I am not sure what you mean in this sentence
Sorry, should have worded it better. The below example should help. Suppose I have a class `MyClass` like below
```
class MyClass:
    def __init__(self, name, age):
        self.name = name
        self.age = age
```
and when I initialise the object `class_ob = MyClass('name', 20)` and print `print(dict(class_obj))` it will print
```
{'name': 'name', 'age': 20}
```
Would that be the same for when `dict(doc)` is called from the issue description
I suggest you to run the example in description and look at the difference between calling:

* print(f'{doc.dict()=}')
* print(f'{dict(doc)}')


In your example it will be the same, there will be a real difference only if there is a nested doc 
Ok. Will run the code and check the difference out. But shouldn't it not print the attributes of the inherited classes as well?
yes it should
Ran the code from the issue description and got the below output
```
doc.dict()={'id': '31a728bdc668421ecad26148484d4812', 'l': [{'id': 'ce15f8dfd26b4d1bfb7599e07c81d358', 'x': 1}, {'id': '2da3af3f506536d3d4b939e147ab3b61', 'x': 2}]}
dict(doc)={'id': '31a728bdc668421ecad26148484d4812', 'l': <DocList[InnerDoc] (length=2)>}
```
The stark difference I see between the two `dict` methods is `doc.dict()` would print the attribute details even when there is nesting but `dict(doc)` prints the attributes on the same level as the object but when there is nesting it will only print the type of the attribute which has the nesting, like how it has done for `'l': <DocList[InnerDoc] (length=2)>`. Have I understood it correctly?
Please review the initial draft of the doc changes I want to propose
1. `dict(doc)`: The `dict()` method when called with the argument of type `BaseDoc` returns the class attribute values set. Let us consider the below example
```
class MyDoc(BaseDoc):
   my_attribute: str

doc_object = MyDoc(my_attribute='my_attribute_value')
print(f'{dict(doc_object)=}')
```
The above code example prints the following
```
{'id': '7072482715ad496140b488120c4e2f98', 'my_attribute': 'my_attribute_value'}
```
It is also worth noting that when there are attributes with types such as `List`, `Dict`, `Tuple`, `DocList` or other types which can have nested objects, then the output of this method will only specify the type of the object. Consider the below example
```
from docarray import BaseDoc, DocList

class ParentDoc(BaseDoc):
   parent_attribute: str

class MyDoc(BaseDoc):
   my_attribute: DocList[ParentDoc]

doc_object = MyDoc(my_attribute=[ParentDoc(parent_attribute='first'), ParentDoc(parent_attribute='second')])
print(f'{dict(doc_object)=}')
```
which will output 
```
{'id': '5e0cad5cf70c14873ead84c54f610358', 'my_attribute': <DocList[ParentDoc] (length=2)>}
```

2. `doc.dict()`: The `dict()` method when called on an object of type `BaseDoc` or it's inherited classes will print out the fields and the contained values even when there is nesting. Let us consider the below example
```
from docarray import BaseDoc, DocList

class ParentDoc(BaseDoc):
   parent_attribute: str

class MyDoc(BaseDoc):
   my_attribute: DocList[ParentDoc]

doc_object = MyDoc(my_attribute=[ParentDoc(parent_attribute='first'), ParentDoc(parent_attribute='second')])
print(f'{doc_object.dict()=}')
```
The output of the `print(f'{doc_object.dict()=}')` will print the values contained within the `my_attribute` class attribute
```
{'id': 'fc902ad3d53f270c1a0d6c7be3220141', 'my_attribute': [{'id': '02d4156a11b6f345cb7185b2a88ebb0c', 'parent_attribute': 'first'}, {'id': '8bf3dc388965a72d2bd102c6dc1201ec', 'parent_attribute': 'second'}]}
```


@samsja Would be awesome if you can take a look at the initial draft and suggest changes. Thanks!
hey @punndcoder28 this looks nice, could you open a PR so that I can suggest you edit directly there ?

Thanks for you contribution :rocket: 



Resolving patch:
diff --git a/docarray/base_doc/doc.py b/docarray/base_doc/doc.py
--- a/docarray/base_doc/doc.py
+++ b/docarray/base_doc/doc.py
@@ -311,7 +311,11 @@ def dict(
     ) -> 'DictStrAny':
         """
         Generate a dictionary representation of the model, optionally specifying
-        which fields to include or exclude.
+        which fields to include or exclude. The method also includes the attributes
+        and their values when attributes are objects of class types which can include
+        nesting. This method differs from the `dict()` method of python which only
+        prints the methods and attributes of the object and only the type of the
+        attribute when attributes are types of other class.
 
         """
 


Test patch: