動機
之前欠的要補…
希望你知道…
- interpreter的結構
- scheme(或lisp)
class的演化
lambda的部分是interpreter的一部份code
其實這裡的重點是struct(這裡叫record)有什麼,只要了解為什麼struct這樣設計大概就會了解到底發生什麼事
class & instance
把東西分成class與class之下的instance
instance有
- 其所屬的class
- attribute的值
class有
- class var的symbol(名字)
- class var的值
- attribute的symbol(名字)
- method的表(method_name => real_proc)
(define-record instance (class i-vals))
(define-record class (c-vars c-vals i-vars m-env))
(lambda (exp env class inst)
(variant-case exp
(i-varref (var)
(lookup var
(class->i-vars (something->value class))
(instance->i-vals (something->value inst))))
(c-varref (var)
(lookup var
(class->c-vars (something->value class))
(class->c-vals (something->value class))))
(i-varassign (var exp)
(let ((value (eval-exp exp env class inst)))
(void->expressed
(assign! var
value
(class->i-vars (something->value class))
(instance->i-vals (something->value inst))))))
(c-varassign (var exp)
(let ((value (eval-exp exp env class inst))
(c-vals (class->c-vals (something->value class))))
(void->expressed
(assign! var
value
(class->c-vars (something->value class))
c-vals))))
(method (formals body)
(let ((new-formals (cons 'self formals)))
(open-method->expressed
(lambda (class-thunk) ;; class has not instanced, wait for the class here
(lambda (args)
(eval-exp
body
(extend-env new-formals (map expressed->denoted args) env)
(make-something (class-thunk))
(make-something (car args))))))))
(meth-app (name rands)
(let ((args (map (lambda (rand)
(eval-exp rand env class inst))
rands)))
(meth-call name (instance->class (car args)) args)))
(new-simpleinst (class-exp)
(let ((inst-class (eval-exp class-exp env class inst)))
(let ((new-inst (make-instance inst-class
(make-vals (class->i-vars inst-class)))))
(ignore (meth-call 'initialize inst-class (list new-inst)))
new-inst)))
(new-simpleclass (c-vars i-vars methdecls init-exp)
(let ((open-methods
(map (lambda (decl)
(expressed->open-method
(eval-exp (decl->exp decl) env class inst)))
methdecls)))
(letrec ((new-class
(make-class
c-vars
(make-vals c-vars)
i-vars
(extend-method-env
(map decl->var methdecls)
(map (lambda (open-meth)
(open-meth (lambda () new-class)))
open-methods)
init-meth-env))))
(ignore (eval-exp init-exp
env
(make-something new-class)
(make-nothing)))
new-class)))))
inherence
現在class可以往上找,所以多一個parent指到另一個class
(define-record instance (class i-vals))
(define-record class (parent c-vars c-vals i-vars m-env))
(lambda (exp env class inst)
(variant-case exp
(new-class (parent-exp c-vars i-vars methdecls init-exp)
(let ((parent-class (eval-exp parent-exp env class inst))
(open-methods
(map (lambda (decl)
(expressed->open-method
(eval-exp (decl->exp decl) env class inst)))
methdecls)))
(let ((new-c-vars (append c-vars (class->c-vars parent-class)))
(new-i-vars (append i-vars (class->i-vars parent-class)))
(new-c-vals ; NEW!
(make-shared-c-vals (class->c-vals parent-class) c-vars)))
(letrec ((new-class
(make-class
(make-something parent-class)
new-c-vars
new-c-vals
new-i-vars
(extend-method-env
(map decl->var methdecls)
(map (lambda (open-method)
(open-method (lambda () new-class)))
open-methods)
(class->m-env parent-class)))))
(ignore
(eval-exp init-exp
env
(make-something new-class)
(make-nothing)))
new-class))))
(super-meth-app (name rands)
(let ((args (map (lambda (x) (eval-exp x env class inst)) rands)))
(meth-call name
(something->value
(class->parent
(something->value class)))
(cons (something->value inst) args))))
;; Figure 7.1.3 : page 219
(i-varref (var)
(lookup var
(class->i-vars (something->value class))
(instance->i-vals (something->value inst))))
(c-varref (var)
(lookup var
(class->c-vars (something->value class))
(class->c-vals (something->value class))))
(i-varassign (var exp)
(let ((value (eval-exp exp env class inst)))
(void->expressed
(assign! var
value
(class->i-vars (something->value class))
(instance->i-vals (something->value inst))))))
(c-varassign (var exp)
(let ((value (eval-exp exp env class inst))
(c-vals (class->c-vals (something->value class))))
(void->expressed
(assign! var
value
(class->c-vars (something->value class))
c-vals))))
(method (formals body)
(let ((new-formals (cons 'self formals)))
(open-method->expressed
(lambda (class-thunk)
(lambda (args)
(eval-exp
body
(extend-env new-formals (map expressed->denoted args) env)
(make-something (class-thunk))
(make-something (car args))))))))
(meth-app (name rands)
(let ((args (map (lambda (rand)
(eval-exp rand env class inst))
rands)))
(meth-call name (instance->class (car args)) args)))
(new-simpleinst (class-exp)
(let ((inst-class (eval-exp class-exp env class inst)))
(let ((new-inst (make-instance inst-class
(make-vals (class->i-vars inst-class)))))
(ignore (meth-call 'initialize inst-class (list new-inst)))
new-inst)))
;; The following is a convenience to make old examples work (stackclass).
;; Syntax-expand can't handle simpleclass, because then it would be
;; expanded away in section 7.1
(new-simpleclass (c-vars i-vars methdecls init-exp)
(eval-exp (make-new-class (make-varref 'baseobject)
c-vars i-vars methdecls init-exp)
env class inst))))
metaclass
從繼承那邊可以看出來,其實parent與class(struct中的ptr)很像,都是往被指到的東西中找值,所以其實我們也不用區分是class還是instance
把class與instance和在一起,會發現,我們不需要c-vals與c-vars,因為
- 如果要instance變數,就看i-vals
- 如果要class變數,就看class的i-vals
到這裡就可以知道,為什麼python與ruby的class都可以有自己的變數,同時可以在某種程度上當class變數用
(define-record instance (class parent i-vars m-env i-vals))
(lambda (exp env class inst)
(variant-case exp
(c-varref (var)
(lookup var
(class->i-vars
(something->value
(instance->class
(something->value class))))
(instance->i-vals
(something->value class))))
(c-varassign (var exp)
(let ((value (eval-exp exp env class inst)))
(void->expressed
(assign! var
value
(class->i-vars
(something->value
(instance->class
(something->value class))))
(instance->i-vals
(something->value class))))))
(new-instance (class-exp parent-exp i-vars methdecls)
(let ((inst-class (eval-exp class-exp env class inst))
(parent-class (eval-exp parent-exp env class inst))
(open-methods
(map (lambda (decl)
(expressed->open-method
(eval-exp (decl->exp decl) env class inst)))
methdecls)))
(let ((new-i-vars (append i-vars (class->i-vars parent-class))))
(letrec ((new-inst
(make-instance
(make-something inst-class)
(make-something parent-class)
new-i-vars
(extend-method-env (map decl->var methdecls)
(map (lambda (open-method)
(open-method (lambda () new-inst)))
open-methods)
(class->m-env parent-class))
(make-vals (class->i-vars inst-class)))))
(ignore (meth-call 'initialize inst-class (list new-inst)))
new-inst))))
;; Figure 7.2.4 : page 228
(super-meth-app (name rands)
(let ((args (map (lambda (x) (eval-exp x env class inst)) rands)))
(meth-call name
(something->value
(class->parent
(something->value class)))
(cons (something->value inst) args))))
;; Figure 7.1.3 : page 219
(i-varref (var)
(lookup var
(class->i-vars (something->value class))
(instance->i-vals (something->value inst))))
(i-varassign (var exp)
(let ((value (eval-exp exp env class inst)))
(void->expressed
(assign! var
value
(class->i-vars (something->value class))
(instance->i-vals (something->value inst))))))
(method (formals body)
(let ((new-formals (cons 'self formals)))
(open-method->expressed
(lambda (class-thunk)
(lambda (args)
(eval-exp
body
(extend-env new-formals (map expressed->denoted args) env)
(make-something (class-thunk))
(make-something (car args))))))))
(meth-app (name rands)
(let ((args (map (lambda (rand)
(eval-exp rand env class inst))
rands)))
(meth-call name
(something->value (instance->class (car args)))
args)))))
python
從剛剛metaclass的struct來看,先不管i-vals與i-vars與m-env
看到下面的code的class C 可以發現 B 是 parent,而A是 class
再看到type.__new__(ins, name,parent,attrs)
attrs就是Dict,可以放值或是function,可以當成i-vals與i-vars與m-env的結合
所以ins是 class, parent是parent,這裡生的就是instance
剛剛是把python的metaclass與之前說的metaclass做結合, 剩下的部分就是__new__與__init__,__new__就是會丟出instance,而__init__就是instance的終點 另外super就是找繼承鍊上的instance,詳情可以看Ref的文章
class A(type):
def __new__(ins, name, parent, attrs):
print("in A")
attrs['ins_var_of_C'] = 101
ins.class_var_of_C = '123'
ret = type.__new__(ins, name,parent,attrs)
return ret
def from_A(self):
return 1000
class B(object):
def __new__(ins):
print("in B")
ins.b = 19
return super().__new__(ins)
class C(B,metaclass=A):
def __new__(ins):
print("in C")
ins.from_here = 876
return super().__new__(ins)
def __init__(self):
self.a = 999
C.from_A()
c = C()
print(c.b)
print(c.a)
print(c.from_here)
print(C.ins_var_of_C)
print(C.class_var_of_C)
print(c.ins_var_of_C)
ruby
與python不同的是,class的部分(metaclass in Python),沒有指定的機會,Ruby會自己生
但是Ruby有eval來做許多壞事
instance_eval 就是用 之前提到的instance來做eval,可以想像成用instance的所有資料作為跑code的環境 class_eval 其實就是會檢查 執行的obj是不是class,其實用{class}.instance_eval是等於{class}.class_eval的
class Object
# The hidden singleton lurks behind everyone
def metaclass; class << self; self; end; end
def meta_eval &blk; metaclass.instance_eval &blk; end
# Adds methods to a metaclass
def meta_def name, &blk
meta_eval { define_method name, &blk }
end
# Defines an instance method within a class
def class_def name, &blk
class_eval { define_method name, &blk }
end
end
class MailTruck
def self.company( name )
meta_def :company do; name; end
# 吃當下的class的metaclass來加method,也就是替被執行該method的class加一個class method,叫company
end
end
class HappyTruck < MailTruck
company "Happy's -- We Bring the Mail, and That's It!"
end
class Creature
def self.traits( *arr )
return @traits if arr.empty?
attr_accessor *arr
arr.each do |trait|
meta_def trait do |val|
@traits ||= {}
@traits[trait] = val
end
end
class_def :initialize do
self.class.traits.each do |k,v|
instance_variable_set( "@#{k}", v )
end
end
class_def :hi do
self.class.traits.each do |k,_|
puts "#{self.instance_eval "self.#{k}"}"
end
end
end
end
class A < Creature
traits :a, :b, :c
end
A.a 10
A.b 20
A.c 30
x = A.new
y = A.new
puts "x content"
x.hi
puts "y content"
y.hi
puts "change"
y.b = 100
y.hi
Ref
EoPL1 Ch10 Metaprogramming in Ruby 你不知道的 super