鸭子类型 多态 源码 grpc 认证 鸭子模型 函数返回值为接口 传输压缩 自定义令牌身份认证
1
继承和多态 - 廖雪峰的官方网站 https://www.liaoxuefeng.com/wiki/1016959663602400/1017497232674368
python与鸭子类型
部分参考来源:作者:JasonDing https://www.jianshu.com/p/650485b78d11##s1
首先介绍下面向对象(OOP)的三大特征:
(1)面向对象程序设计有三大特征:封装(Encapsulation)、继承(Inheritance)、多态(Polymorphism)。这三个单词很常见,大家还是记住为好!
(2)封装(Encapsulation):类包含了数据和方法,将数据和方法放在一个类中就构成了封装。
(3)继承(Inheritance):Java是单继承的(这点和C++有区别),意味着一个类只能继承于一个类,被继承的类叫父类(或者叫基类,base class),继承的类叫子类。Java中的继承使用关键字extends。但是,一个类可以实现多个接口,多个接口之间用逗号进行分割。实现接口使用关键字implements。
(4)多态(Polymorphism):多态最核心的思想就是,父类的引用可以指向子类的对象,或者接口类型的引用可以指向实现该接口的类的实例。多态之所以是这样的是因为基于一个事实:子类就是父类!
(5)关于多态的一些重要说明:
- 当使用多态方式调用方法时,首先检查父类中是否有此方法,如果没有则编译错误,如果有则再去调用子类重写(Override)【如果重写的话】的此方法,没有重写的话,还是调用从父类继承过来的方法。
- 两种类型的强制类型转换:
- 向上类型转换(upcast):将子类型引用转换成父类型引用。对于向上类型转换不需要显示指定。
- 向下类型转换(downcast):将父类型引用转换成子类型引用。对于向下类型转换,必须要显示指定。向下类型转换的原则:父类型引用指向谁才能转换成谁。
- 多态是一种运行期的行为,不是编译期行为!在编译期间它只知道是一个引用,只有到了执行期,引用才知道指向的是谁。这就是所谓的“软绑定”。
- 多态是一项让程序员“将改变的事物和未改变的事物分离开来”重要技术。
鸭子类型:
鸭子类型是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法和属性的集合"决定。这个概念的名字来源于由James Whitcomb Riley提出的鸭子测试,“鸭子测试”可以这样表述:“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”
在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的。例如,在不使用鸭子类型的语言中,我们可以编写一个函数,它接受一个类型为"鸭子"的对象,并调用它的"走"和"叫"方法。在使用鸭子类型的语言中,这样的一个函数可以接受一个任意类型的对象,并调用它的"走"和"叫"方法。如果这些需要被调用的方法不存在,那么将引发一个运行时错误。任何拥有这样的正确的"走"和"叫"方法的对象都可被函数接受的这种行为引出了以上表述,这种决定类型的方式因此得名。
鸭子类型通常得益于不测试方法和函数中参数的类型,而是依赖文档、清晰的代码和测试来确保正确使用。
Duck typing 这个概念来源于美国印第安纳州的诗人詹姆斯·惠特科姆·莱利(James Whitcomb Riley,1849-
1916)的诗句:”When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.”
先上代码,也是来源于网上很经典的案例:
1 class Duck(): 2 def walk(self): 3 print('I walk like a duck') 4 def swim(self): 5 print('i swim like a duck') 6 7 class Person(): 8 def walk(self): 9 print('this one walk like a duck') 10 def swim(self): 11 print('this man swim like a duck')
可以很明显的看出,Person
类拥有跟Duck
类一样的方法,当有一个函数调用Duck
类,并利用到了两个方法walk()
和swim()
。我们传入Person
类也一样可以运行,函数并不会检查对象的类型是不是Duck
,只要他拥有walk()
和swim()
方法,就可以正确的被调用。
再举例,如果一个对象实现了__getitem__
方法,那python的解释器就会把它当做一个collection
,就可以在这个对象上使用切片,获取子项等方法;如果一个对象实现了__iter__
和next
方法,python就会认为它是一个iterator
,就可以在这个对象上通过循环来获取各个子项。
python与鸭子类型 - Guo磊 - 博客园 https://www.cnblogs.com/guolei2570/p/8830934.html
package main
import "fmt"
// declare interface
type Dog interface {
Bark()
}
// declare struct
type Dalmatian struct {
DogType string
}
// implement the interface
func (d Dalmatian) Bark() {
fmt.Println("Dalmatian barking!!")
}
func MakeDogBark(d Dog) {
d.Bark()
}
func main() {
d := Dalmatian{"Jack"}
MakeDogBark(d) // Dalmatian barking!!
}
Polymorphism in GoLang - GoLang Docs https://golangdocs.com/polymorphism-in-golang
Polymorphism in GoLang
In Object-Oriented Programming, an object can behave like another object. This property is called polymorphism. This post will cover how we achieve polymorphism in GoLang.
Table of Contents
What is polymorphism?
Polymorphism is a property that is available to many OO-languages. Go despite not being an OO-language achieves polymorphism through interfaces.
Polymorphism using interfaces
In GoLang, polymorphism is achieved mainly using interfaces. A type implementing a function defined in interface becomes the type defined as an interface. This is the property that makes polymorphism achievable in Go.
Here is an example of polymorphism in action.
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
|
package main import "fmt" // declare interface type Dog interface { Bark() } // declare struct type Dalmatian struct { DogType string } // implement the interface func (d Dalmatian) Bark() { fmt.Println( "Dalmatian barking!!" ) } func MakeDogBark(d Dog) { d.Bark() } func main() { d := Dalmatian{ "Jack" } MakeDogBark(d) // Dalmatian barking!! } |
In the code above, the struct Dalmatian implements the Dog interface. Thus the struct becomes the type Dog and so that it can be passed in that function.
Now, we can simply add any type and implement that interface and the type will behave as that interface. That is polymorphism. An object taking many different forms.
Uses of polymorphism
Polymorphism is used to reduce code in general. There will be less coupling if polymorphism is used. A single function can be used to do the same thing on multiple different objects. This is where polymorphism is heavily used. It is one of the most important concepts in OO-Programming. Go not being a strict OO-language achieves polymorphism in an elegant way.
https://golangbot.com/polymorphism/
源码 grpc 认证 鸭子模型
Authentication | gRPC https://www.grpc.io/docs/guides/auth/#authenticate-with-google
将谷歌的认证实现,改成定义的认证实现
perRPC, _ := oauth.NewServiceAccountFromFile("service-account.json", scope)
google.golang.org/grpc@v1.43.0/credentials/oauth/oauth.go:192
// NewServiceAccountFromFile constructs the PerRPCCredentials using the JSON key file
// of a Google Developers service account.
func NewServiceAccountFromFile(keyFile string, scope ...string) (credentials.PerRPCCredentials, error) {
jsonKey, err := ioutil.ReadFile(keyFile)
if err != nil {
return nil, fmt.Errorf("credentials: failed to read the service account key file: %v", err)
}
return NewServiceAccountFromKey(jsonKey, scope...)
}
关注返回值
google.golang.org/grpc@v1.43.0/credentials/credentials.go:38
// PerRPCCredentials defines the common interface for the credentials which need to
// attach security information to every RPC (e.g., oauth2).
type PerRPCCredentials interface {
// GetRequestMetadata gets the current request metadata, refreshing
// tokens if required. This should be called by the transport layer on
// each request, and the data should be populated in headers or other
// context. If a status code is returned, it will be used as the status
// for the RPC. uri is the URI of the entry point for the request.
// When supported by the underlying implementation, ctx can be used for
// timeout and cancellation. Additionally, RequestInfo data will be
// available via ctx to this call.
// TODO(zhaoq): Define the set of the qualified keys instead of leaving
// it as an arbitrary string.
GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error)
// RequireTransportSecurity indicates whether the credentials requires
// transport security.
RequireTransportSecurity() bool
}
其中原返回值为Google的逻辑
// NewServiceAccountFromKey constructs the PerRPCCredentials using the JSON key slice
// from a Google Developers service account.
func NewServiceAccountFromKey(jsonKey []byte, scope ...string) (credentials.PerRPCCredentials, error) {
config, err := google.JWTConfigFromJSON(jsonKey, scope...)
if err != nil {
return nil, err
}
return &serviceAccount{config: config}, nil
}
实现方法
type T struct {
}
func NewCustomerPerRPCCredentials() (PerRPCCredentials, error) {
return &T{}, nil
}
func (t *T) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
return nil, nil
}
func (t *T) RequireTransportSecurity() bool {
return true
}
perRPC, err := NewCustomerPerRPCCredentials()
鸭子能走路,能走路的就是鸭子。
google.golang.org/grpc@v1.43.0/credentials/oauth/oauth.go:151
// serviceAccount represents PerRPCCredentials via JWT signing key.
type serviceAccount struct {
mu sync.Mutex
config *jwt.Config
t *oauth2.Token
}
func (s *serviceAccount) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
s.mu.Lock()
defer s.mu.Unlock()
if !s.t.Valid() {
var err error
s.t, err = s.config.TokenSource(ctx).Token()
if err != nil {
return nil, err
}
}
ri, _ := credentials.RequestInfoFromContext(ctx)
if err := credentials.CheckSecurityLevel(ri.AuthInfo, credentials.PrivacyAndIntegrity); err != nil {
return nil, fmt.Errorf("unable to transfer serviceAccount PerRPCCredentials: %v", err)
}
return map[string]string{
"authorization": s.t.Type() + " " + s.t.AccessToken,
}, nil
}
func (s *serviceAccount) RequireTransportSecurity() bool {
return true
}
google.golang.org/grpc v1.43.0
google.golang.org/protobuf v1.27.1
package grpc
import (
"context"
"crypto/tls"
"crypto/x509"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/encoding/gzip"
"io/ioutil"
"time"
)
type T struct {
AccessToken string
}
func NewCustomerPerRPCCredentials(AccessToken string) (credentials.PerRPCCredentials, error) {
return &T{AccessToken: AccessToken}, nil
}
func (t *T) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
m := map[string]string{}
m["AccessToken"] = t.AccessToken
return m, nil
}
func (t *T) RequireTransportSecurity() bool {
return true
}
func NewConn() (conn *grpc.ClientConn) {
certFile := "../cert/server1_cert.pem"
//creds, err := credentials.NewClientTLSFromFile(certFile, "")
b, err := ioutil.ReadFile(certFile)
if err != nil {
panic(err)
}
creds_cp := x509.NewCertPool()
if !creds_cp.AppendCertsFromPEM(b) {
panic("credentials: failed to append certificates")
}
creds, err := credentials.NewTLS(&tls.Config{ServerName: "", RootCAs: creds_cp, InsecureSkipVerify: true}), nil
if err != nil {
panic(err)
}
perRPC, err := NewCustomerPerRPCCredentials("Val-ak123")
if err != nil {
panic(err)
}
bytes := 1024 * 1024 * 4 * 4
cp := grpc.ConnectParams{}
cp.MinConnectTimeout = 16 * time.Second
co := []grpc.CallOption{grpc.UseCompressor(gzip.Name)}
opts := []grpc.DialOption{
grpc.WithTransportCredentials(creds),
grpc.WithBlock(),
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(bytes)),
grpc.WithConnectParams(cp),
grpc.WithDefaultCallOptions(co...),
grpc.WithPerRPCCredentials(perRPC),
}
grpc.UseCompressor(gzip.Name)
conn, err = grpc.Dial("1.24.14.14:12345", opts...)
if err != nil {
panic(err)
}
return conn
}
证书不被信任
InsecureSkipVerify: true, // test server certificate is not trusted.
Go/src/crypto/tls/example_test.go:99
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
KeyLogWriter: w,
Rand: zeroSource{}, // for reproducible output; don't do this.
InsecureSkipVerify: true, // test server certificate is not trusted.
},
},
}
// InsecureSkipVerify controls whether a client verifies the server's
// certificate chain and host name. If InsecureSkipVerify is true, crypto/tls
// accepts any certificate presented by the server and any host name in that
// certificate. In this mode, TLS is susceptible to machine-in-the-middle
// attacks unless custom verification is used. This should be used only for
// testing or in combination with VerifyConnection or VerifyPeerCertificate.
InsecureSkipVerify bool
Go/src/crypto/tls/common.go:646
Go当中TLS/SSL 证书的实践 - 知乎 https://zhuanlan.zhihu.com/p/338688506
github.com\robfig\cron\v3@v3.0.0\cron.go
package config import ( "context" "errors" "reflect" "sync" "time" "github.com/go-kratos/kratos/v2/log" // init encoding _ "github.com/go-kratos/kratos/v2/encoding/json" _ "github.com/go-kratos/kratos/v2/encoding/proto" _ "github.com/go-kratos/kratos/v2/encoding/xml" _ "github.com/go-kratos/kratos/v2/encoding/yaml" ) var ( // ErrNotFound is key not found. ErrNotFound = errors.New("key not found") // ErrTypeAssert is type assert error. ErrTypeAssert = errors.New("type assert error") _ Config = (*config)(nil) ) // Observer is config observer. type Observer func(string, Value) // Config is a config interface. type Config interface { Load() error Scan(v interface{}) error Value(key string) Value Watch(key string, o Observer) error Close() error } type config struct { opts options reader Reader cached sync.Map observers sync.Map watchers []Watcher log *log.Helper } // New new a config with options. func New(opts ...Option) Config { o := options{ logger: log.DefaultLogger, decoder: defaultDecoder, resolver: defaultResolver, } for _, opt := range opts { opt(&o) } return &config{ opts: o, reader: newReader(o), log: log.NewHelper(o.logger), } } func (c *config) watch(w Watcher) { for { kvs, err := w.Next() if errors.Is(err, context.Canceled) { c.log.Infof("watcher's ctx cancel : %v", err) return } if err != nil { time.Sleep(time.Second) c.log.Errorf("failed to watch next config: %v", err) continue } if err := c.reader.Merge(kvs...); err != nil { c.log.Errorf("failed to merge next config: %v", err) continue } if err := c.reader.Resolve(); err != nil { c.log.Errorf("failed to resolve next config: %v", err) continue } c.cached.Range(func(key, value interface{}) bool { k := key.(string) v := value.(Value) if n, ok := c.reader.Value(k); ok && !reflect.DeepEqual(n.Load(), v.Load()) { v.Store(n.Load()) if o, ok := c.observers.Load(k); ok { o.(Observer)(k, v) } } return true }) } } func (c *config) Load() error { for _, src := range c.opts.sources { kvs, err := src.Load() if err != nil { return err } for _, v := range kvs { c.log.Infof("config loaded: %s format: %s", v.Key, v.Format) } if err = c.reader.Merge(kvs...); err != nil { c.log.Errorf("failed to merge config source: %v", err) return err } w, err := src.Watch() if err != nil { c.log.Errorf("failed to watch config source: %v", err) return err } c.watchers = append(c.watchers, w) go c.watch(w) } if err := c.reader.Resolve(); err != nil { c.log.Errorf("failed to resolve config source: %v", err) return err } return nil } func (c *config) Value(key string) Value { if v, ok := c.cached.Load(key); ok { return v.(Value) } if v, ok := c.reader.Value(key); ok { c.cached.Store(key, v) return v } return &errValue{err: ErrNotFound} } func (c *config) Scan(v interface{}) error { data, err := c.reader.Source() if err != nil { return err } return unmarshalJSON(data, v) } func (c *config) Watch(key string, o Observer) error { if v := c.Value(key); v.Load() == nil { return ErrNotFound } c.observers.Store(key, o) return nil } func (c *config) Close() error { for _, w := range c.watchers { if err := w.Stop(); err != nil { return err } } return nil }