# 教程：Python和Sage中的对象和类¶

## 其他语言有“变量”¶

 int a = 1; 

 a = 2; 

 int b = a; 

“b”是第二个框，其中有一个整数2的副本。方框“a”有单独的副本。

## Python有“名字”¶

 a = 1 

 a = 2 

 b = a 

sage: a = [1,2,3]
sage: b = a
sage: b[1] = 0
sage: a
[1, 0, 3]


sage: b = 7
sage: b
7
sage: a
[1, 0, 3]


## 面向对象编程范式¶

1. 任何需要由计算机操作的真实（或数学）世界都是由 对象 .

2. 每个对象都是 实例 一些 .

object

class

sage: F = CombinatorialFreeModule(QQ, Permutations())
sage: el = 3*F([1,3,2])+ F([1,2,3])
sage: el
B[[1, 2, 3]] + 3*B[[1, 3, 2]]


（对于每个排列 [1, 3, 2] ，中对应的元素 F 表示为 B[[1, 3, 2]] --在一个 CombinatorialFreeModule ，如果元素的索引 x ，则默认情况下，其打印表示形式为 B[x]

sage: type(el)
<class 'sage.combinat.free_module.CombinatorialFreeModule_with_category.element_class'>


sage: el._monomial_coefficients
{[1, 2, 3]: 1, [1, 3, 2]: 3}


sage: el._monomial_coefficients[Permutation([3,2,1])] = 1/2
sage: el
B[[1, 2, 3]] + 3*B[[1, 3, 2]] + 1/2*B[[3, 2, 1]]


sage: 2*el
2*B[[1, 2, 3]] + 6*B[[1, 3, 2]] + B[[3, 2, 1]]
sage: sorted(el.support())
[[1, 2, 3], [1, 3, 2], [3, 2, 1]]
sage: el.coefficient([1, 2, 3])
1


The behavior is defined through methods (support, coefficient). Note that this is true even for equality, printing or mathematical operations. For example, the call a == b actually is translated to the method call a.__eq__(b). The names of those special methods which are usually called through operators are fixed by the Python language and are of the form __name__. Examples include __eq__ and __le__ for operators == and <=, __repr__ (see 关于课程的Sage细节) for printing, __add__ and __mult__ for operators + and *. See http://docs.python.org/library/ for a complete list.

sage: el.__eq__(F([1,3,2]))
False
sage: el.__repr__()
'B[[1, 2, 3]] + 3*B[[1, 3, 2]] + 1/2*B[[3, 2, 1]]'
sage: el.__mul__(2)
2*B[[1, 2, 3]] + 6*B[[1, 3, 2]] + B[[3, 2, 1]]


sage: from sage.structure.element import Element
sage: class MyElt(Element):
....:     def __init__(self, parent, val):
....:         super(MyElt, self).__init__(parent)
....:         self.value = val
sage: el = MyElt(val=42, parent=ZZ)
sage: el
Generic element of a structure


sage: el.__dict__
{'value': 42}
sage: el.__class__
<class '__main__.MyElt'>


sage: el.rename("bla")
sage: el
bla
sage: el.__dict__
{'__custom_name': 'bla', 'value': 42}


sage: e = Integer(9)
sage: type(e)
<type 'sage.rings.integer.Integer'>
sage: e.__dict__
Traceback (most recent call last):
...
AttributeError: 'sage.rings.integer.Integer' object has no attribute '__dict__'

sage: id4 = SymmetricGroup(4).one()
sage: type(id4)
<type 'sage.groups.perm_gps.permgroup_element.SymmetricGroupElement'>
sage: id4.__dict__
Traceback (most recent call last):
...
AttributeError: 'sage.groups.perm_gps.permgroup_element.SymmetricGroupElement' object has no attribute '__dict__'


sage: el = Integer(9)
sage: id(el)  # random
139813642977744
sage: el1 = el; id(el1) == id(el)
True
sage: el1 is el
True


sage: el2 = Integer(9)
sage: el2 == el1
True
sage: el2 is el1
False
sage: id(el2) == id(el)
False


method

attribute

### 例如：餐馆里的一杯饮料¶

sage: class Glass(object):
....:     def __init__(self, size):
....:         assert size > 0
....:         self._size = float(size)  # an attribute
....:         self._content = float(0.0)  # another attribute
....:     def __repr__(self):
....:         if self._content == 0.0:
....:             return "An empty glass of size %s"%(self._size)
....:         else:
....:             return "A glass of size %s cl containing %s cl of water"%(
....:                     self._size, self._content)
....:     def fill(self):
....:         self._content = self._size
....:     def empty(self):
....:         self._content = float(0.0)


sage: myGlass = Glass(10); myGlass
An empty glass of size 10.0
sage: myGlass.fill(); myGlass
A glass of size 10.0 cl containing 10.0 cl of water
sage: myGlass.empty(); myGlass
An empty glass of size 10.0


1. 类的定义 Glass 定义了两个属性， _size_content . 它定义了四种方法， __init____repr__fillempty . （此类的任何实例也将具有从类继承的其他属性和方法 object . 见 Inheritance 下面）

2. 方法 __init__ 用于初始化对象：它由 构造函数 调用时执行的类 Glass(10) .

3. 方法 __repr__ 返回用于打印对象的字符串，例如，在本例中，在计算 myGlass .

• 大多数情况下，为了保证数据结构的一致性，用户不需要直接更改对象的某些属性。这些属性称为 私有的 . 由于Python中没有确保隐私的机制，约定如下：私有属性的名称以下划线开头。

• 因此，属性访问只能通过方法进行。读取或写入私有属性的方法称为访问器。

• 只供内部使用的方法也以下划线作为前缀。

### 练习¶

1. 添加方法 is_empty 如果杯子是空的，则返回true。

2. 定义方法 drink 带参数 amount 它能让人部分地喝下杯子里的水。如果一个人要求喝比杯子里更多的水或负量的水，就提出一个错误。

3. 允许杯子装满葡萄酒、啤酒或其他饮料。方法 fill 应接受参数 beverage . 饮料存储在属性中 _beverage . 更新方法 __repr__ 因此。

4. 添加属性 _clean 和方法 is_cleanwash . 在创造一个玻璃是干净的，一灌满它就变脏了，它必须经过清洗才能再次变得干净。

5. 测试一切。

6. 确保一切都经过测试。

7. 再测试一遍。

## 遗传¶

sage: class AbstractDish(object):
....:     def __init__(self):
....:         self._clean = True
....:     def is_clean(self):
....:         return self._clean
....:     def state(self):
....:         return "clean" if self.is_clean() else "dirty"
....:     def __repr__(self):
....:         return "An unspecified %s dish"%self.state()
....:     def _make_dirty(self):
....:         self._clean = False
....:     def wash(self):
....:         self._clean = True


sage: class Spoon(AbstractDish):  # Spoon inherits from AbstractDish
....:     def __repr__(self):
....:         return "A %s spoon"%self.state()
....:     def eat_with(self):
....:         self._make_dirty()


sage: s = Spoon(); s
A clean spoon
sage: s.is_clean()
True
sage: s.eat_with(); s
A dirty spoon
sage: s.is_clean()
False
sage: s.wash(); s
A clean spoon


### 总结¶

1. 任何类都可以重用另一个类的行为。有人说 继承 从超类或者说它 派生 从它开始。

2. 子类的任何实例也是其超类的实例：

sage: type(s)
<class '__main__.Spoon'>
sage: isinstance(s, Spoon)
True
sage: isinstance(s, AbstractDish)
True

3. 如果子类重新定义了一个方法，那么它将替换前一个方法。有人说 超载 方法。但是可以显式地调用隐藏超类方法。

sage: s.__repr__()
'A clean spoon'
sage: Spoon.__repr__(s)
'A clean spoon'
sage: AbstractDish.__repr__(s)
'An unspecified clean dish'


sage: super(Spoon, s).__repr__()
'An unspecified clean dish'


sage: class Spoon(AbstractDish):
....:     def __init__(self):
....:         print("Building a spoon")
....:         super(Spoon, self).__init__()
....:     def __repr__(self):
....:         return "A %s spoon"%self.state()
....:     def eat_with(self):
....:         self._make_dirty()
sage: s = Spoon()
Building a spoon
sage: s
A clean spoon


### 练习¶

1. 修改类 Glasses 所以它继承了 Dish .

2. 写一节课 Plate 它的实例可以和类一起包含任何一顿饭 Fork . 尽可能避免代码重复（提示：您可以编写因子分解类 ContainerDish

3. 测试一切。

## 关于课程的Sage细节¶

• Sage中数学对象的任何类都应该继承自 SageObject 而不是来自 object . 大多数时候，它们实际上是从一个子类继承的，比如 ParentElement .

• 印刷应该通过 _repr_ 而不是 __repr__ 允许重命名。

• 一般来说，Sage特有的特殊方法通常被命名为 _meth_ 而不是 __meth__ . 例如，许多类实现 _hash_ 由使用和缓存 __hash__ . 同样地，一个群体中的元素通常是 _mul_ ，这样就不需要像在 __mul__ .

## 练习的解决方案¶

1. 以下是第一个练习的解决方案：

sage: class Glass(object):
....:     def __init__(self, size):
....:         assert size > 0
....:         self._size = float(size)
....:         self.wash()
....:     def __repr__(self):
....:         if self._content == 0.0:
....:             return "An empty glass of size %s"%(self._size)
....:         else:
....:             return "A glass of size %s cl containing %s cl of %s"%(
....:                     self._size, self._content, self._beverage)
....:     def content(self):
....:         return self._content
....:     def beverage(self):
....:         return self._beverage
....:     def fill(self, beverage = "water"):
....:         if not self.is_clean():
....:             raise ValueError("Don't want to fill a dirty glass")
....:         self._clean = False
....:         self._content = self._size
....:         self._beverage = beverage
....:     def empty(self):
....:         self._content = float(0.0)
....:     def is_empty(self):
....:         return self._content == 0.0
....:     def drink(self, amount):
....:         if amount <= 0.0:
....:             raise ValueError("amount must be positive")
....:         elif amount > self._content:
....:             raise ValueError("not enough beverage in the glass")
....:         else:
....:             self._content -= float(amount)
....:     def is_clean(self):
....:         return self._clean
....:     def wash(self):
....:         self._content = float(0.0)
....:         self._beverage = None
....:         self._clean = True

2. 让我们检查一下是否一切正常：

sage: G = Glass(10.0)
sage: G
An empty glass of size 10.0
sage: G.is_empty()
True
sage: G.drink(2)
Traceback (most recent call last):
...
ValueError: not enough beverage in the glass
sage: G.fill("beer")
sage: G
A glass of size 10.0 cl containing 10.0 cl of beer
sage: G.is_empty()
False
sage: G.is_clean()
False
sage: G.drink(5.0)
sage: G
A glass of size 10.0 cl containing 5.0 cl of beer
sage: G.is_empty()
False
sage: G.is_clean()
False
sage: G.drink(5)
sage: G
An empty glass of size 10.0
sage: G.is_clean()
False
sage: G.fill("orange juice")
Traceback (most recent call last):
...
ValueError: Don't want to fill a dirty glass
sage: G.wash()
sage: G
An empty glass of size 10.0
sage: G.fill("orange juice")
sage: G
A glass of size 10.0 cl containing 10.0 cl of orange juice

3. 下面是第二个练习的解决方法：

sage: class AbstractDish(object):
....:     def __init__(self):
....:         self._clean = True
....:     def is_clean(self):
....:         return self._clean
....:     def state(self):
....:         return "clean" if self.is_clean() else "dirty"
....:     def __repr__(self):
....:         return "An unspecified %s dish"%self.state()
....:     def _make_dirty(self):
....:         self._clean = False
....:     def wash(self):
....:         self._clean = True

sage: class ContainerDish(AbstractDish):
....:     def __init__(self, size):
....:         assert size > 0
....:         self._size = float(size)
....:         self._content = float(0)
....:         super(ContainerDish, self).__init__()
....:     def content(self):
....:         return self._content
....:     def empty(self):
....:         self._content = float(0.0)
....:     def is_empty(self):
....:         return self._content == 0.0
....:     def wash(self):
....:         self._content = float(0.0)
....:         super(ContainerDish, self).wash()

sage: class Glass(ContainerDish):
....:     def __repr__(self):
....:         if self._content == 0.0:
....:             return "An empty glass of size %s"%(self._size)
....:         else:
....:             return "A glass of size %s cl containing %s cl of %s"%(
....:                     self._size, self._content, self._beverage)
....:     def beverage(self):
....:         return self._beverage
....:     def fill(self, beverage = "water"):
....:         if not self.is_clean():
....:             raise ValueError("Don't want to fill a dirty glass")
....:         self._make_dirty()
....:         self._content = self._size
....:         self._beverage = beverage
....:     def drink(self, amount):
....:         if amount <= 0.0:
....:             raise ValueError("amount must be positive")
....:         elif amount > self._content:
....:             raise ValueError("not enough beverage in the glass")
....:         else:
....:             self._content -= float(amount)
....:     def wash(self):
....:         self._beverage = None
....:         super(Glass, self).wash()

4. 让我们检查一下是否一切正常：

sage: G = Glass(10.0)
sage: G
An empty glass of size 10.0
sage: G.is_empty()
True
sage: G.drink(2)
Traceback (most recent call last):
...
ValueError: not enough beverage in the glass
sage: G.fill("beer")
sage: G
A glass of size 10.0 cl containing 10.0 cl of beer
sage: G.is_empty()
False
sage: G.is_clean()
False
sage: G.drink(5.0)
sage: G
A glass of size 10.0 cl containing 5.0 cl of beer
sage: G.is_empty()
False
sage: G.is_clean()
False
sage: G.drink(5)
sage: G
An empty glass of size 10.0
sage: G.is_clean()
False
sage: G.fill("orange juice")
Traceback (most recent call last):
...
ValueError: Don't want to fill a dirty glass
sage: G.wash()
sage: G
An empty glass of size 10.0
sage: G.fill("orange juice")
sage: G
A glass of size 10.0 cl containing 10.0 cl of orange juice