-
Notifications
You must be signed in to change notification settings - Fork 25
Expand file tree
/
Copy pathPythonObject.java
More file actions
93 lines (84 loc) · 3.15 KB
/
PythonObject.java
File metadata and controls
93 lines (84 loc) · 3.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
package plang;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* The runtime state of an object in Python.
*/
public class PythonObject {
private final Map<String,PythonObject> attrs = new HashMap<>();
private final PythonType type;
private List<PythonObject> mro;
PythonObject(PythonType type) {
this.type = type;
}
/**
* The Python type (i.e. class) of this object.
*
* May be null if this object is itself a class. This is a simplification for this assignment;
* in actual Python, _all_ objects have types, and the type of a class is `type`. That is itself
* a class, so the type of `type` is `type`. Phew! Thank goodness we’re ignoring that.
*/
public final PythonType getType() {
return type;
}
/**
* Return the list of objects we should search when asked for a given attribute, in the order
* we should search them.
*
* The real Python implementation of the MRO is substantially more complicated, because
* (1) Python supports multiple inheritance (i.e. classes can have multiple base classes), and
* (2) the base of `type` is `object`, and the type of `object` is `type`, which creates a
* circular reference that Python resolves by special-casing both `object` and `type`.
*
* Once again, hooray for not having to deal with that.
*/
public List<PythonObject> getMRO() {
if(mro == null)
mro = Collections.unmodifiableList(buildMRO());
return mro;
}
/**
* Constructs the MRO. Called only once, the first time we need the MRO; this class memoizes the
* result (i.e. it remembers the list buildMRO() returned and keeps returning it).
*/
protected List<PythonObject> buildMRO() {
ArrayList<PythonObject> temp = new ArrayList<>();
temp.add(this);
if(type != null)
temp.addAll(type.getMRO());
return temp;
}
/**
* Returns the value of the attribute with the given name for this object.
*
* @param attrName The name of the attribute to look for.
* @return Its value if found.
* @throws PythonAttributeException When there is no attribute on this object with that name.
*/
public final PythonObject get(String attrName) throws PythonAttributeException {
for(PythonObject pyob : getMRO()){
if(pyob.attrs.containsKey(attrName)){
return pyob.attrs.get(attrName);
}
}
throw new PythonAttributeException(this, attrName);
}
/**
* Add or changes the value of an attribute on this object. Note that it sets the value for
* _this_ object alone, even if the attribute already exists somewhere upstream in the attribute
* resolution order.
*
* @param attrName The name of the attribute to set
* @param value Its new value
*/
public final void set(String attrName, PythonObject value) {
attrs.put(attrName, value);
}
@Override
public String toString() {
return "PythonObject<" + getType().getName() + ">" + attrs;
}
}